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 return OsmReader.parseDataSet(compression.getUncompressedInputStream(in), progressMonitor.createSubTaskMonitor(1, false)); 203 } 204 } 205 206 protected class OsmChangeParser extends Parser<DataSet> { 207 protected OsmChangeParser(ProgressMonitor progressMonitor, Compression compression) { 208 super(progressMonitor, compression); 209 } 210 211 @Override 212 public DataSet parse() throws OsmTransferException, IllegalDataException, IOException { 213 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(9, false)); 214 if (in == null) 215 return null; 216 progressMonitor.subTask(tr("Downloading OSM data...")); 217 return OsmChangeReader.parseDataSet(compression.getUncompressedInputStream(in), progressMonitor.createSubTaskMonitor(1, false)); 218 } 219 } 220 221 protected class GpxParser extends Parser<GpxData> { 222 protected GpxParser(ProgressMonitor progressMonitor, Compression compression) { 223 super(progressMonitor, compression); 224 } 225 226 @Override 227 public GpxData parse() throws OsmTransferException, IllegalDataException, IOException, SAXException { 228 in = getInputStreamRaw(url, progressMonitor.createSubTaskMonitor(1, true), null, true); 229 if (in == null) 230 return null; 231 progressMonitor.subTask(tr("Downloading OSM data...")); 232 GpxReader reader = new GpxReader(compression.getUncompressedInputStream(in)); 233 gpxParsedProperly = reader.parse(false); 234 GpxData result = reader.getGpxData(); 235 result.fromServer = isGpxFromServer(url); 236 return result; 237 } 238 } 239 240 protected class NoteParser extends Parser<List<Note>> { 241 242 public NoteParser(ProgressMonitor progressMonitor, Compression compression) { 243 super(progressMonitor, compression); 244 } 245 246 @Override 247 public List<Note> parse() throws OsmTransferException, IllegalDataException, IOException, SAXException { 248 in = getInputStream(url, progressMonitor.createSubTaskMonitor(1, true)); 249 if (in == null) { 250 return Collections.emptyList(); 251 } 252 progressMonitor.subTask(tr("Downloading OSM notes...")); 253 NoteReader reader = new NoteReader(compression.getUncompressedInputStream(in)); 254 return reader.parse(); 255 } 256 } 257 258 /** 259 * Determines if the given URL denotes an OSM gpx-related API call. 260 * @param url The url to check 261 * @return true if the url matches "Trace ID" API call or "Trackpoints bbox" API call, false otherwise 262 * @see GpxData#fromServer 263 * @since 12679 264 */ 265 public static final boolean isGpxFromServer(String url) { 266 return url != null && (url.matches(GpxUrlPattern.TRACE_ID.pattern()) || url.matches(GpxUrlPattern.TRACKPOINTS_BBOX.pattern())); 267 } 268}