001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.IOException; 007import java.io.InputStream; 008import java.util.Collections; 009import java.util.List; 010 011import org.openstreetmap.josm.data.gpx.GpxData; 012import org.openstreetmap.josm.data.notes.Note; 013import org.openstreetmap.josm.data.osm.DataSet; 014import org.openstreetmap.josm.gui.progress.ProgressMonitor; 015import org.openstreetmap.josm.tools.Utils; 016import org.xml.sax.SAXException; 017 018/** 019 * Read content from OSM server for a given URL 020 * @since 1146 021 */ 022public class OsmServerLocationReader extends OsmServerReader { 023 024 // CHECKSTYLE.OFF: MethodParamPad 025 // CHECKSTYLE.OFF: SingleSpaceSeparator 026 027 /** 028 * Patterns for OSM data download URLs. 029 * @since 12679 030 */ 031 public enum OsmUrlPattern { 032 OSM_API_URL ("https?://.*/api/0.6/(map|nodes?|ways?|relations?|\\*).*"), 033 OVERPASS_API_URL ("https?://.*/interpreter\\?data=.*"), 034 OVERPASS_API_XAPI_URL ("https?://.*/xapi(\\?.*\\[@meta\\]|_meta\\?).*"), 035 EXTERNAL_OSM_FILE ("https?://.*/.*\\.osm"); 036 037 private final String urlPattern; 038 039 OsmUrlPattern(String urlPattern) { 040 this.urlPattern = urlPattern; 041 } 042 043 /** 044 * Returns the URL pattern. 045 * @return the URL pattern 046 */ 047 public String pattern() { 048 return urlPattern; 049 } 050 } 051 052 /** 053 * Patterns for GPX download URLs. 054 * @since 12679 055 */ 056 public enum GpxUrlPattern { 057 TRACE_ID ("https?://.*(osm|openstreetmap).org/trace/\\p{Digit}+/data"), 058 USER_TRACE_ID("https?://.*(osm|openstreetmap).org/user/[^/]+/traces/(\\p{Digit}+)"), 059 EDIT_TRACE_ID("https?://.*(osm|openstreetmap).org/edit/?\\?gpx=(\\p{Digit}+)(#.*)?"), 060 061 TRACKPOINTS_BBOX("https?://.*/api/0.6/trackpoints\\?bbox=.*,.*,.*,.*"), 062 TASKING_MANAGER("https?://.*/api/v\\p{Digit}+/project/\\p{Digit}+/tasks_as_gpx?.*"), 063 064 EXTERNAL_GPX_SCRIPT("https?://.*exportgpx.*"), 065 EXTERNAL_GPX_FILE ("https?://.*/(.*\\.gpx)"); 066 067 private final String urlPattern; 068 069 GpxUrlPattern(String urlPattern) { 070 this.urlPattern = urlPattern; 071 } 072 073 /** 074 * Returns the URL pattern. 075 * @return the URL pattern 076 */ 077 public String pattern() { 078 return urlPattern; 079 } 080 } 081 082 /** 083 * Patterns for Note download URLs. 084 * @since 12679 085 */ 086 public enum NoteUrlPattern { 087 /** URL of OSM API Notes endpoint */ 088 API_URL ("https?://.*/api/0.6/notes.*"), 089 /** URL of OSM API Notes compressed dump file */ 090 DUMP_FILE("https?://.*/(.*\\.osn(\\.(gz|xz|bz2?|zip))?)"); 091 092 private final String urlPattern; 093 094 NoteUrlPattern(String urlPattern) { 095 this.urlPattern = urlPattern; 096 } 097 098 /** 099 * Returns the URL pattern. 100 * @return the URL pattern 101 */ 102 public String pattern() { 103 return urlPattern; 104 } 105 } 106 107 // CHECKSTYLE.ON: SingleSpaceSeparator 108 // CHECKSTYLE.ON: MethodParamPad 109 110 protected final String url; 111 112 /** 113 * Constructs a new {@code OsmServerLocationReader}. 114 * @param url The URL to fetch 115 */ 116 public OsmServerLocationReader(String url) { 117 this.url = url; 118 } 119 120 protected abstract static class Parser<T> { 121 protected final ProgressMonitor progressMonitor; 122 protected final Compression compression; 123 protected InputStream in; 124 125 public Parser(ProgressMonitor progressMonitor, Compression compression) { 126 this.progressMonitor = progressMonitor; 127 this.compression = compression; 128 } 129 130 public abstract T parse() throws OsmTransferException, IllegalDataException, IOException, SAXException; 131 } 132 133 protected final <T> T doParse(Parser<T> parser, final ProgressMonitor progressMonitor) throws OsmTransferException { 134 progressMonitor.beginTask(tr("Contacting Server...", 10)); 135 try { 136 return parser.parse(); 137 } catch (OsmTransferException e) { 138 throw e; 139 } catch (IOException | SAXException | IllegalDataException e) { 140 if (cancel) 141 return null; 142 throw new OsmTransferException(e); 143 } finally { 144 progressMonitor.finishTask(); 145 activeConnection = null; 146 Utils.close(parser.in); 147 parser.in = null; 148 } 149 } 150 151 @Override 152 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 153 return parseOsm(progressMonitor, Compression.NONE); 154 } 155 156 @Override 157 public DataSet parseOsm(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 158 return doParse(new OsmParser(progressMonitor, compression), progressMonitor); 159 } 160 161 @Override 162 public DataSet parseOsmChange(ProgressMonitor progressMonitor) throws OsmTransferException { 163 return parseOsmChange(progressMonitor, Compression.NONE); 164 } 165 166 @Override 167 public DataSet parseOsmChange(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 168 return doParse(new OsmChangeParser(progressMonitor, compression), progressMonitor); 169 } 170 171 @Override 172 public GpxData parseRawGps(ProgressMonitor progressMonitor) throws OsmTransferException { 173 return parseRawGps(progressMonitor, Compression.NONE); 174 } 175 176 @Override 177 public GpxData parseRawGps(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 178 return doParse(new GpxParser(progressMonitor, compression), progressMonitor); 179 } 180 181 @Override 182 public List<Note> parseRawNotes(ProgressMonitor progressMonitor) throws OsmTransferException { 183 return parseRawNotes(progressMonitor, Compression.NONE); 184 } 185 186 @Override 187 public List<Note> parseRawNotes(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 188 return doParse(new NoteParser(progressMonitor, compression), progressMonitor); 189 } 190 191 protected class OsmParser extends Parser<DataSet> { 192 protected OsmParser(ProgressMonitor progressMonitor, Compression compression) { 193 super(progressMonitor, compression); 194 } 195 196 @Override 197 public DataSet parse() throws OsmTransferException, IllegalDataException, IOException { 198 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(9, false)); 199 if (in == null) 200 return null; 201 progressMonitor.subTask(tr("Downloading OSM data...")); 202 InputStream uncompressedInputStream = compression.getUncompressedInputStream(in); 203 ProgressMonitor subTaskMonitor = progressMonitor.createSubTaskMonitor(1, false); 204 if ("application/json".equals(contentType)) { 205 return OsmJsonReader.parseDataSet(uncompressedInputStream, subTaskMonitor); 206 } else { 207 return OsmReader.parseDataSet(uncompressedInputStream, subTaskMonitor); 208 } 209 } 210 } 211 212 protected class OsmChangeParser extends Parser<DataSet> { 213 protected OsmChangeParser(ProgressMonitor progressMonitor, Compression compression) { 214 super(progressMonitor, compression); 215 } 216 217 @Override 218 public DataSet parse() throws OsmTransferException, IllegalDataException, IOException { 219 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(9, false)); 220 if (in == null) 221 return null; 222 progressMonitor.subTask(tr("Downloading OSM data...")); 223 return OsmChangeReader.parseDataSet(compression.getUncompressedInputStream(in), progressMonitor.createSubTaskMonitor(1, false)); 224 } 225 } 226 227 protected class GpxParser extends Parser<GpxData> { 228 protected GpxParser(ProgressMonitor progressMonitor, Compression compression) { 229 super(progressMonitor, compression); 230 } 231 232 @Override 233 public GpxData parse() throws OsmTransferException, IllegalDataException, IOException, SAXException { 234 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(1, true), null, true); 235 if (in == null) 236 return null; 237 progressMonitor.subTask(tr("Downloading OSM data...")); 238 GpxReader reader = new GpxReader(compression.getUncompressedInputStream(in)); 239 gpxParsedProperly = reader.parse(false); 240 GpxData result = reader.getGpxData(); 241 result.fromServer = isGpxFromServer(url); 242 return result; 243 } 244 } 245 246 protected class NoteParser extends Parser<List<Note>> { 247 248 public NoteParser(ProgressMonitor progressMonitor, Compression compression) { 249 super(progressMonitor, compression); 250 } 251 252 @Override 253 public List<Note> parse() throws OsmTransferException, IllegalDataException, IOException, SAXException { 254 in = getInputStream(url, progressMonitor.createSubTaskMonitor(1, true)); 255 if (in == null) { 256 return Collections.emptyList(); 257 } 258 progressMonitor.subTask(tr("Downloading OSM notes...")); 259 NoteReader reader = new NoteReader(compression.getUncompressedInputStream(in)); 260 return reader.parse(); 261 } 262 } 263 264 /** 265 * Determines if the given URL denotes an OSM gpx-related API call. 266 * @param url The url to check 267 * @return true if the url matches "Trace ID" API call or "Trackpoints bbox" API call, false otherwise 268 * @see GpxData#fromServer 269 * @since 12679 270 */ 271 public static final boolean isGpxFromServer(String url) { 272 return url != null && (url.matches(GpxUrlPattern.TRACE_ID.pattern()) || url.matches(GpxUrlPattern.TRACKPOINTS_BBOX.pattern())); 273 } 274}