001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.io; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.io.InputStream; 007 import java.text.MessageFormat; 008 import java.util.ArrayList; 009 import java.util.Collection; 010 011 import org.openstreetmap.josm.data.osm.DataSet; 012 import org.openstreetmap.josm.data.osm.DataSetMerger; 013 import org.openstreetmap.josm.data.osm.OsmPrimitive; 014 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 015 import org.openstreetmap.josm.data.osm.Relation; 016 import org.openstreetmap.josm.data.osm.Way; 017 import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 018 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 019 import org.openstreetmap.josm.tools.CheckParameterUtil; 020 021 /** 022 * OsmServerBackreferenceReader fetches the primitives from the OSM server which 023 * refer to a specific primitive. For a {@link Node}, ways and relations are retrieved 024 * which refer to the node. For a {@link Way} or a {@link Relation}, only relations are 025 * read. 026 * 027 * OsmServerBackreferenceReader uses the API calls <code>[node|way|relation]/#id/relations</code> 028 * and <code>node/#id/ways</code> to retrieve the referring primitives. The default behaviour 029 * of these calls is to reply incomplete primitives only. 030 * 031 * If you set {@link #setReadFull(boolean)} to true this reader uses a {@link MultiFetchServerObjectReader} 032 * to complete incomplete primitives. 033 * 034 * 035 */ 036 public class OsmServerBackreferenceReader extends OsmServerReader { 037 038 /** the id of the primitive whose referrers are to be read */ 039 private long id; 040 /** the type of the primitive */ 041 private OsmPrimitiveType primitiveType; 042 /** true if this reader should complete incomplete primitives */ 043 private boolean readFull; 044 045 /** 046 * constructor 047 * 048 * @param primitive the primitive to be read. Must not be null. primitive.id > 0 expected 049 * 050 * @exception IllegalArgumentException thrown if primitive is null 051 * @exception IllegalArgumentException thrown if primitive.id <= 0 052 */ 053 public OsmServerBackreferenceReader(OsmPrimitive primitive) throws IllegalArgumentException { 054 CheckParameterUtil.ensureValidPrimitiveId(primitive, "primitive"); 055 this.id = primitive.getId(); 056 this.primitiveType = OsmPrimitiveType.from(primitive); 057 this.readFull = false; 058 } 059 060 /** 061 * constructor 062 * 063 * @param id the id of the primitive. > 0 expected 064 * @param type the type of the primitive. Must not be null. 065 * 066 * @exception IllegalArgumentException thrown if id <= 0 067 * @exception IllegalArgumentException thrown if type is null 068 * 069 */ 070 public OsmServerBackreferenceReader(long id, OsmPrimitiveType type) throws IllegalArgumentException { 071 if (id <= 0) 072 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected. Got ''{1}''.", "id", id)); 073 CheckParameterUtil.ensureParameterNotNull(type, "type"); 074 this.id = id; 075 this.primitiveType = type; 076 this.readFull = false; 077 } 078 079 /** 080 * constructor 081 * 082 * @param id the id of the primitive. > 0 expected 083 * @param readFull true, if referers should be read fully (i.e. including their immediate children) 084 * 085 */ 086 public OsmServerBackreferenceReader(OsmPrimitive primitive, boolean readFull) { 087 this(primitive); 088 this.readFull = readFull; 089 } 090 091 /** 092 * constructor 093 * 094 * @param primitive the primitive whose referers are to be read 095 * @param readFull true, if referers should be read fully (i.e. including their immediate children) 096 * 097 * @exception IllegalArgumentException thrown if id <= 0 098 * @exception IllegalArgumentException thrown if type is null 099 * 100 */ 101 public OsmServerBackreferenceReader(long id, OsmPrimitiveType type, boolean readFull) throws IllegalArgumentException { 102 this(id, type); 103 this.readFull = false; 104 } 105 106 /** 107 * Replies true if this reader also reads immediate children of referring primitives 108 * 109 * @return true if this reader also reads immediate children of referring primitives 110 */ 111 public boolean isReadFull() { 112 return readFull; 113 } 114 115 /** 116 * Set true if this reader should reads immediate children of referring primitives too. False, otherweise. 117 * 118 * @param readFull true if this reader should reads immediate children of referring primitives too. False, otherweise. 119 */ 120 public void setReadFull(boolean readFull) { 121 this.readFull = readFull; 122 } 123 124 /** 125 * Reads referring ways from the API server and replies them in a {@link DataSet} 126 * 127 * @return the data set 128 * @throws OsmTransferException 129 */ 130 protected DataSet getReferringWays(ProgressMonitor progressMonitor) throws OsmTransferException { 131 InputStream in = null; 132 progressMonitor.beginTask(null, 2); 133 try { 134 progressMonitor.indeterminateSubTask(tr("Downloading from OSM Server...")); 135 StringBuffer sb = new StringBuffer(); 136 sb.append(primitiveType.getAPIName()) 137 .append("/").append(id).append("/ways"); 138 139 in = getInputStream(sb.toString(), progressMonitor.createSubTaskMonitor(1, true)); 140 if (in == null) 141 return null; 142 progressMonitor.subTask(tr("Downloading referring ways ...")); 143 return OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, true)); 144 } catch(OsmTransferException e) { 145 throw e; 146 } catch (Exception e) { 147 if (cancel) 148 return null; 149 throw new OsmTransferException(e); 150 } finally { 151 progressMonitor.finishTask(); 152 if (in != null) { 153 try { 154 in.close(); 155 } catch(Exception e) {} 156 activeConnection = null; 157 } 158 } 159 } 160 161 /** 162 * Reads referring relations from the API server and replies them in a {@link DataSet} 163 * 164 * @param progressMonitor the progress monitor 165 * @return the data set 166 * @throws OsmTransferException 167 */ 168 protected DataSet getReferringRelations(ProgressMonitor progressMonitor) throws OsmTransferException { 169 InputStream in = null; 170 progressMonitor.beginTask(null, 2); 171 try { 172 progressMonitor.subTask(tr("Contacting OSM Server...")); 173 StringBuffer sb = new StringBuffer(); 174 sb.append(primitiveType.getAPIName()) 175 .append("/").append(id).append("/relations"); 176 177 in = getInputStream(sb.toString(), progressMonitor.createSubTaskMonitor(1, true)); 178 if (in == null) 179 return null; 180 progressMonitor.subTask(tr("Downloading referring relations ...")); 181 return OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, true)); 182 } catch(OsmTransferException e) { 183 throw e; 184 } catch (Exception e) { 185 if (cancel) 186 return null; 187 throw new OsmTransferException(e); 188 } finally { 189 progressMonitor.finishTask(); 190 if (in != null) { 191 try { 192 in.close(); 193 } catch(Exception e) {} 194 activeConnection = null; 195 } 196 } 197 } 198 199 /** 200 * Scans a dataset for incomplete primitives. Depending on the configuration of this reader 201 * incomplete primitives are read from the server with an individual <tt>/api/0.6/[way,relation]/#id/full</tt> 202 * request. 203 * 204 * <ul> 205 * <li>if this reader reads referers for a {@link Node}, referring ways are always 206 * read individually from the server</li> 207 * <li>if this reader reads referers for an {@link Way} or a {@link Relation}, referring relations 208 * are only read fully if {@link #setReadFull(boolean)} is set to true.</li> 209 * </ul> 210 * 211 * The method replies the modified dataset. 212 * 213 * @param ds the original dataset 214 * @param progressMonitor the progress monitor 215 * @return the modified dataset 216 * @throws OsmTransferException thrown if an exception occurs. 217 */ 218 protected DataSet readIncompletePrimitives(DataSet ds, ProgressMonitor progressMonitor) throws OsmTransferException { 219 progressMonitor.beginTask(null, 2); 220 try { 221 Collection<Way> waysToCheck = new ArrayList<Way>(ds.getWays()); 222 if (isReadFull() ||primitiveType.equals(OsmPrimitiveType.NODE)) { 223 for (Way way: waysToCheck) { 224 if (!way.isNew() && way.hasIncompleteNodes()) { 225 OsmServerObjectReader reader = new OsmServerObjectReader(way.getId(), OsmPrimitiveType.from(way), true /* read full */); 226 DataSet wayDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 227 DataSetMerger visitor = new DataSetMerger(ds, wayDs); 228 visitor.merge(); 229 } 230 } 231 } 232 if (isReadFull()) { 233 Collection<Relation> relationsToCheck = new ArrayList<Relation>(ds.getRelations()); 234 for (Relation relation: relationsToCheck) { 235 if (!relation.isNew() && relation.hasIncompleteMembers()) { 236 OsmServerObjectReader reader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.from(relation), true /* read full */); 237 DataSet wayDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false)); 238 DataSetMerger visitor = new DataSetMerger(ds, wayDs); 239 visitor.merge(); 240 } 241 } 242 } 243 return ds; 244 } finally { 245 progressMonitor.finishTask(); 246 } 247 } 248 249 /** 250 * Reads the referring primitives from the OSM server, parses them and 251 * replies them as {@link DataSet} 252 * 253 * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null. 254 * @return the dataset with the referring primitives 255 * @exception OsmTransferException thrown if an error occurs while communicating with the server 256 */ 257 @Override 258 public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException { 259 if (progressMonitor == null) { 260 progressMonitor = NullProgressMonitor.INSTANCE; 261 } 262 try { 263 progressMonitor.beginTask(null, 3); 264 DataSet ret = new DataSet(); 265 if (primitiveType.equals(OsmPrimitiveType.NODE)) { 266 DataSet ds = getReferringWays(progressMonitor.createSubTaskMonitor(1, false)); 267 DataSetMerger visitor = new DataSetMerger(ret,ds); 268 visitor.merge(); 269 ret = visitor.getTargetDataSet(); 270 } 271 DataSet ds = getReferringRelations(progressMonitor.createSubTaskMonitor(1, false)); 272 DataSetMerger visitor = new DataSetMerger(ret,ds); 273 visitor.merge(); 274 ret = visitor.getTargetDataSet(); 275 readIncompletePrimitives(ret, progressMonitor.createSubTaskMonitor(1, false)); 276 if (ret != null) { 277 ret.deleteInvisible(); 278 } 279 return ret; 280 } finally { 281 progressMonitor.finishTask(); 282 } 283 } 284 }