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 */ 065 EXTERNAL_GPX_SCRIPT("https?://.*exportgpx.*"), 066 /** External GPX file */ 067 EXTERNAL_GPX_FILE ("https?://.*/(.*\\.gpx)"); 068 069 private final String urlPattern; 070 071 GpxUrlPattern(String urlPattern) { 072 this.urlPattern = urlPattern; 073 } 074 075 /** 076 * Returns the URL pattern. 077 * @return the URL pattern 078 */ 079 public String pattern() { 080 return urlPattern; 081 } 082 } 083 084 /** 085 * Patterns for Note download URLs. 086 * @since 12679 087 */ 088 public enum NoteUrlPattern { 089 /** URL of OSM API Notes endpoint */ 090 API_URL ("https?://.*/api/0.6/notes.*"), 091 /** URL of OSM API Notes compressed dump file */ 092 DUMP_FILE("https?://.*/(.*\\.osn(\\.(gz|xz|bz2?|zip))?)"); 093 094 private final String urlPattern; 095 096 NoteUrlPattern(String urlPattern) { 097 this.urlPattern = urlPattern; 098 } 099 100 /** 101 * Returns the URL pattern. 102 * @return the URL pattern 103 */ 104 public String pattern() { 105 return urlPattern; 106 } 107 } 108 109 // CHECKSTYLE.ON: SingleSpaceSeparator 110 // CHECKSTYLE.ON: MethodParamPad 111 112 protected final String url; 113 114 /** 115 * Constructs a new {@code OsmServerLocationReader}. 116 * @param url The URL to fetch 117 */ 118 public OsmServerLocationReader(String url) { 119 this.url = url; 120 } 121 122 /** 123 * Returns the URL to fetch 124 * @return the URL to fetch 125 * @since 15247 126 */ 127 public final String getUrl() { 128 return url; 129 } 130 131 protected abstract static class Parser<T> { 132 protected final ProgressMonitor progressMonitor; 133 protected final Compression compression; 134 protected InputStream in; 135 136 public Parser(ProgressMonitor progressMonitor, Compression compression) { 137 this.progressMonitor = progressMonitor; 138 this.compression = compression; 139 } 140 141 public abstract T parse() throws OsmTransferException, IllegalDataException, IOException, SAXException; 142 } 143 144 protected final <T> T doParse(Parser<T> parser, final ProgressMonitor progressMonitor) throws OsmTransferException { 145 progressMonitor.beginTask(tr("Contacting Server...", 10)); 146 try { // NOPMD 147 return parser.parse(); 148 } catch (OsmTransferException e) { 149 throw e; 150 } catch (IOException | SAXException | IllegalDataException e) { 151 if (cancel) 152 return null; 153 throw new OsmTransferException(e); 154 } finally { 155 progressMonitor.finishTask(); 156 activeConnection = null; 157 Utils.close(parser.in); 158 parser.in = null; 159 } 160 } 161 162 @Override 163 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 164 return parseOsm(progressMonitor, Compression.NONE); 165 } 166 167 @Override 168 public DataSet parseOsm(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 169 return doParse(new OsmParser(progressMonitor, compression), progressMonitor); 170 } 171 172 @Override 173 public DataSet parseOsmChange(ProgressMonitor progressMonitor) throws OsmTransferException { 174 return parseOsmChange(progressMonitor, Compression.NONE); 175 } 176 177 @Override 178 public DataSet parseOsmChange(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 179 return doParse(new OsmChangeParser(progressMonitor, compression), progressMonitor); 180 } 181 182 @Override 183 public GpxData parseRawGps(ProgressMonitor progressMonitor) throws OsmTransferException { 184 return parseRawGps(progressMonitor, Compression.NONE); 185 } 186 187 @Override 188 public GpxData parseRawGps(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 189 return doParse(new GpxParser(progressMonitor, compression), progressMonitor); 190 } 191 192 @Override 193 public List<Note> parseRawNotes(ProgressMonitor progressMonitor) throws OsmTransferException { 194 return parseRawNotes(progressMonitor, Compression.NONE); 195 } 196 197 @Override 198 public List<Note> parseRawNotes(ProgressMonitor progressMonitor, Compression compression) throws OsmTransferException { 199 return doParse(new NoteParser(progressMonitor, compression), progressMonitor); 200 } 201 202 protected class OsmParser extends Parser<DataSet> { 203 protected OsmParser(ProgressMonitor progressMonitor, Compression compression) { 204 super(progressMonitor, compression); 205 } 206 207 @Override 208 public DataSet parse() throws OsmTransferException, IllegalDataException, IOException { 209 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(9, false)); 210 if (in == null) 211 return null; 212 progressMonitor.subTask(tr("Downloading OSM data...")); 213 InputStream uncompressedInputStream = compression.getUncompressedInputStream(in); // NOPMD 214 ProgressMonitor subTaskMonitor = progressMonitor.createSubTaskMonitor(1, false); 215 if ("application/json".equals(contentType)) { 216 return OsmJsonReader.parseDataSet(uncompressedInputStream, subTaskMonitor); 217 } else { 218 return OsmReader.parseDataSet(uncompressedInputStream, subTaskMonitor); 219 } 220 } 221 } 222 223 protected class OsmChangeParser extends Parser<DataSet> { 224 protected OsmChangeParser(ProgressMonitor progressMonitor, Compression compression) { 225 super(progressMonitor, compression); 226 } 227 228 @Override 229 public DataSet parse() throws OsmTransferException, IllegalDataException, IOException { 230 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(9, false)); 231 if (in == null) 232 return null; 233 progressMonitor.subTask(tr("Downloading OSM data...")); 234 return OsmChangeReader.parseDataSet(compression.getUncompressedInputStream(in), progressMonitor.createSubTaskMonitor(1, false)); 235 } 236 } 237 238 protected class GpxParser extends Parser<GpxData> { 239 protected GpxParser(ProgressMonitor progressMonitor, Compression compression) { 240 super(progressMonitor, compression); 241 } 242 243 @Override 244 public GpxData parse() throws OsmTransferException, IllegalDataException, IOException, SAXException { 245 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(1, true), null, true); 246 if (in == null) 247 return null; 248 progressMonitor.subTask(tr("Downloading OSM data...")); 249 GpxReader reader = new GpxReader(compression.getUncompressedInputStream(in)); 250 gpxParsedProperly = reader.parse(false); 251 GpxData result = reader.getGpxData(); 252 result.fromServer = isGpxFromServer(url); 253 return result; 254 } 255 } 256 257 protected class NoteParser extends Parser<List<Note>> { 258 259 public NoteParser(ProgressMonitor progressMonitor, Compression compression) { 260 super(progressMonitor, compression); 261 } 262 263 @Override 264 public List<Note> parse() throws OsmTransferException, IllegalDataException, IOException, SAXException { 265 in = getInputStream(url, progressMonitor.createSubTaskMonitor(1, true)); 266 if (in == null) { 267 return Collections.emptyList(); 268 } 269 progressMonitor.subTask(tr("Downloading OSM notes...")); 270 NoteReader reader = new NoteReader(compression.getUncompressedInputStream(in)); 271 return reader.parse(); 272 } 273 } 274 275 /** 276 * Determines if the given URL denotes an OSM gpx-related API call. 277 * @param url The url to check 278 * @return true if the url matches "Trace ID" API call or "Trackpoints bbox" API call, false otherwise 279 * @see GpxData#fromServer 280 * @since 12679 281 */ 282 public static final boolean isGpxFromServer(String url) { 283 return url != null && (url.matches(GpxUrlPattern.TRACE_ID.pattern()) || url.matches(GpxUrlPattern.TRACKPOINTS_BBOX.pattern())); 284 } 285}