Changeset 28820 in osm for applications/editors/josm/plugins/reverter/src
- Timestamp:
- 2012-10-16T20:45:01+02:00 (12 years ago)
- Location:
- applications/editors/josm/plugins/reverter/src/reverter
- Files:
-
- 1 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/reverter/src/reverter/MultiOsmReader.java
r28819 r28820 1 package reverter.corehacks; 2 3 import static org.openstreetmap.josm.tools.I18n.tr; 1 package reverter; 4 2 5 3 import java.io.InputStream; 6 4 import java.io.InputStreamReader; 7 import java.text.MessageFormat;8 import java.util.ArrayList;9 import java.util.Collection;10 import java.util.HashMap;11 import java.util.LinkedList;12 import java.util.List;13 import java.util.Map;14 import java.util.logging.Level;15 import java.util.logging.Logger;16 5 17 import javax.xml.parsers.ParserConfigurationException; 18 import javax.xml.parsers.SAXParserFactory; 6 import javax.xml.stream.XMLInputFactory; 7 import javax.xml.stream.XMLStreamException; 8 import javax.xml.stream.XMLStreamReader; 19 9 20 import org.openstreetmap.josm.data.Bounds;21 import org.openstreetmap.josm.data.coor.LatLon;22 import org.openstreetmap.josm.data.osm.DataSet;23 import org.openstreetmap.josm.data.osm.DataSource;24 import org.openstreetmap.josm.data.osm.Node;25 import org.openstreetmap.josm.data.osm.NodeData;26 import org.openstreetmap.josm.data.osm.OsmPrimitive;27 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;28 import org.openstreetmap.josm.data.osm.PrimitiveData;29 import org.openstreetmap.josm.data.osm.PrimitiveId;30 import org.openstreetmap.josm.data.osm.Relation;31 import org.openstreetmap.josm.data.osm.RelationData;32 import org.openstreetmap.josm.data.osm.RelationMember;33 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;34 import org.openstreetmap.josm.data.osm.Storage;35 import org.openstreetmap.josm.data.osm.User;36 import org.openstreetmap.josm.data.osm.Way;37 import org.openstreetmap.josm.data.osm.WayData;38 import org.openstreetmap.josm.gui.progress.ProgressMonitor;39 10 import org.openstreetmap.josm.io.IllegalDataException; 40 import org.openstreetmap.josm.io.OsmDataParsingException;41 11 import org.openstreetmap.josm.io.OsmReader; 42 import org.openstreetmap.josm.tools.DateUtils; 43 import org.xml.sax.Attributes; 44 import org.xml.sax.InputSource; 45 import org.xml.sax.Locator; 46 import org.xml.sax.SAXException; 47 import org.xml.sax.helpers.DefaultHandler; 12 import org.openstreetmap.josm.io.UTFInputStreamReader; 48 13 49 14 /** 50 * Modified{@see org.openstreetmap.josm.io.OsmReader} that can handle multiple XML streams.15 * Subclass of {@see org.openstreetmap.josm.io.OsmReader} that can handle multiple XML streams. 51 16 * 52 17 */ 53 public class MultiOsmReader { 54 static private final Logger logger = Logger.getLogger(OsmReader.class.getName()); 55 56 /** 57 * The dataset to add parsed objects to. 58 */ 59 private final DataSet ds = new DataSet(); 60 61 /** 62 * Replies the parsed data set 63 * 64 * @return the parsed data set 65 */ 66 public DataSet getDataSet() { 67 return ds; 68 } 69 70 /** the map from external ids to read OsmPrimitives. External ids are 71 * longs too, but in contrast to internal ids negative values are used 72 * to identify primitives unknown to the OSM server 73 */ 74 private Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 75 76 /** 77 * constructor (for private use only) 78 * 79 * @see #parseDataSet(InputStream, DataSet, ProgressMonitor) 80 * @see #parseDataSetOsm(InputStream, DataSet, ProgressMonitor) 81 */ 82 public MultiOsmReader() { 83 externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 84 } 85 86 /** 87 * Used as a temporary storage for relation members, before they 88 * are resolved into pointers to real objects. 89 */ 90 private static class RelationMemberData { 91 public OsmPrimitiveType type; 92 public long id; 93 public String role; 94 } 95 96 /** 97 * Data structure for the remaining way objects 98 */ 99 private final Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>(); 100 101 /** 102 * Data structure for relation objects 103 */ 104 private final Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>(); 105 106 private class Parser extends DefaultHandler { 107 private Locator locator; 108 109 @Override 110 public void setDocumentLocator(Locator locator) { 111 this.locator = locator; 112 } 113 114 protected void throwException(String msg) throws OsmDataParsingException{ 115 throw new OsmDataParsingException(msg).rememberLocation(locator); 116 } 117 /** 118 * The current osm primitive to be read. 119 */ 120 private OsmPrimitive currentPrimitive; 121 private long currentExternalId; 122 private String generator; 123 private final Storage<String> internedStrings = new Storage<String>(); 124 125 // Memory optimization - see #2312 126 private String intern(String s) { 127 String result = internedStrings.get(s); 128 if (result == null) { 129 internedStrings.put(s); 130 return s; 131 } else 132 return result; 133 } 134 135 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 136 137 if (qName.equals("osm")) { 138 if (atts == null) { 139 throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "version", "osm")); 140 } 141 String v = atts.getValue("version"); 142 if (v == null) { 143 throwException(tr("Missing mandatory attribute ''{0}''.", "version")); 144 } 145 if (!(v.equals("0.5") || v.equals("0.6"))) { 146 throwException(tr("Unsupported version: {0}", v)); 147 } 148 // save generator attribute for later use when creating DataSource objects 149 generator = atts.getValue("generator"); 150 ds.setVersion(v); 151 152 } else if (qName.equals("bounds")) { 153 // new style bounds. 154 String minlon = atts.getValue("minlon"); 155 String minlat = atts.getValue("minlat"); 156 String maxlon = atts.getValue("maxlon"); 157 String maxlat = atts.getValue("maxlat"); 158 String origin = atts.getValue("origin"); 159 if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 160 if (origin == null) { 161 origin = generator; 162 } 163 Bounds bounds = new Bounds( 164 new LatLon(Double.parseDouble(minlat), Double.parseDouble(minlon)), 165 new LatLon(Double.parseDouble(maxlat), Double.parseDouble(maxlon))); 166 DataSource src = new DataSource(bounds, origin); 167 ds.dataSources.add(src); 168 } else { 169 throwException(tr( 170 "Missing mandatory attributes on element ''bounds''. Got minlon=''{0}'',minlat=''{1}'',maxlon=''{3}'',maxlat=''{4}'', origin=''{5}''.", 171 minlon, minlat, maxlon, maxlat, origin 172 )); 173 } 174 175 // ---- PARSING NODES AND WAYS ---- 176 177 } else if (qName.equals("node")) { 178 NodeData nd = new NodeData(); 179 nd.setCoor(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon"))); 180 readCommon(atts, nd); 181 Node n = new Node(nd.getId(), nd.getVersion()); 182 n.load(nd); 183 n.setVisible(nd.isVisible()); 184 externalIdMap.put(nd.getPrimitiveId(), n); 185 currentPrimitive = n; 186 currentExternalId = nd.getUniqueId(); 187 } else if (qName.equals("way")) { 188 WayData wd = new WayData(); 189 readCommon(atts, wd); 190 Way w = new Way(wd.getId(), wd.getVersion()); 191 w.load(wd); 192 w.setVisible(wd.isVisible()); 193 externalIdMap.put(wd.getPrimitiveId(), w); 194 ways.put(wd.getUniqueId(), new ArrayList<Long>()); 195 currentPrimitive = w; 196 currentExternalId = wd.getUniqueId(); 197 } else if (qName.equals("nd")) { 198 Collection<Long> list = ways.get(currentExternalId); 199 if (list == null) { 200 throwException( 201 tr("Found XML element <nd> not as direct child of element <way>.") 202 ); 203 } 204 if (atts.getValue("ref") == null) { 205 throwException( 206 tr("Missing mandatory attribute ''{0}'' on <nd> of way {1}.", "ref", currentPrimitive.getUniqueId()) 207 ); 208 } 209 long id = getLong(atts, "ref"); 210 if (id == 0) { 211 throwException( 212 tr("Illegal value of attribute ''ref'' of element <nd>. Got {0}.", id) 213 ); 214 } 215 if (currentPrimitive.isDeleted()) { 216 logger.info(tr("Deleted way {0} contains nodes", currentPrimitive.getUniqueId())); 217 } else { 218 list.add(id); 219 } 220 221 // ---- PARSING RELATIONS ---- 222 223 } else if (qName.equals("relation")) { 224 RelationData rd = new RelationData(); 225 readCommon(atts, rd); 226 Relation r = new Relation(rd.getId(), rd.getVersion()); 227 r.load(rd); 228 r.setVisible(rd.isVisible()); 229 externalIdMap.put(rd.getPrimitiveId(), r); 230 relations.put(rd.getUniqueId(), new LinkedList<RelationMemberData>()); 231 currentPrimitive = r; 232 currentExternalId = rd.getUniqueId(); 233 } else if (qName.equals("member")) { 234 Collection<RelationMemberData> list = relations.get(currentExternalId); 235 if (list == null) { 236 throwException( 237 tr("Found XML element <member> not as direct child of element <relation>.") 238 ); 239 } 240 RelationMemberData emd = new RelationMemberData(); 241 String value = atts.getValue("ref"); 242 if (value == null) { 243 throwException(tr("Missing attribute ''ref'' on member in relation {0}.",currentPrimitive.getUniqueId())); 244 } 245 try { 246 emd.id = Long.parseLong(value); 247 } catch(NumberFormatException e) { 248 throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(currentPrimitive.getUniqueId()),value)); 249 } 250 value = atts.getValue("type"); 251 if (value == null) { 252 throwException(tr("Missing attribute ''type'' on member {0} in relation {1}.", Long.toString(emd.id), Long.toString(currentPrimitive.getUniqueId()))); 253 } 254 try { 255 emd.type = OsmPrimitiveType.fromApiTypeName(value); 256 } catch(IllegalArgumentException e) { 257 throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", Long.toString(emd.id), Long.toString(currentPrimitive.getUniqueId()), value)); 258 } 259 value = atts.getValue("role"); 260 emd.role = value; 261 262 if (emd.id == 0) { 263 throwException(tr("Incomplete <member> specification with ref=0")); 264 } 265 266 if (currentPrimitive.isDeleted()) { 267 logger.info(tr("Deleted relation {0} contains members", currentPrimitive.getUniqueId())); 268 } else { 269 list.add(emd); 270 } 271 272 // ---- PARSING TAGS (applicable to all objects) ---- 273 274 } else if (qName.equals("tag")) { 275 String key = atts.getValue("k"); 276 String value = atts.getValue("v"); 277 currentPrimitive.put(intern(key), intern(value)); 278 279 } else { 280 System.out.println(tr("Undefined element ''{0}'' found in input stream. Skipping.", qName)); 281 } 282 } 283 284 private double getDouble(Attributes atts, String value) { 285 return Double.parseDouble(atts.getValue(value)); 286 } 287 288 private User createUser(String uid, String name) throws SAXException { 289 if (uid == null) { 290 if (name == null) 291 return null; 292 return User.createLocalUser(name); 293 } 294 try { 295 long id = Long.parseLong(uid); 296 return User.createOsmUser(id, name); 297 } catch(NumberFormatException e) { 298 throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid)); 299 } 300 return null; 301 } 302 /** 303 * Read out the common attributes from atts and put them into this.current. 304 */ 305 void readCommon(Attributes atts, PrimitiveData current) throws SAXException { 306 current.setId(getLong(atts, "id")); 307 if (current.getUniqueId() == 0) { 308 throwException(tr("Illegal object with ID=0.")); 309 } 310 311 String time = atts.getValue("timestamp"); 312 if (time != null && time.length() != 0) { 313 current.setTimestamp(DateUtils.fromString(time)); 314 } 315 316 // user attribute added in 0.4 API 317 String user = atts.getValue("user"); 318 // uid attribute added in 0.6 API 319 String uid = atts.getValue("uid"); 320 current.setUser(createUser(uid, user)); 321 322 // visible attribute added in 0.4 API 323 String visible = atts.getValue("visible"); 324 if (visible != null) { 325 current.setVisible(Boolean.parseBoolean(visible)); 326 } 327 328 String versionString = atts.getValue("version"); 329 int version = 0; 330 if (versionString != null) { 331 try { 332 version = Integer.parseInt(versionString); 333 } catch(NumberFormatException e) { 334 throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString)); 335 } 336 if (ds.getVersion().equals("0.6")){ 337 if (version <= 0 && current.getUniqueId() > 0) { 338 throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString)); 339 } else if (version < 0 && current.getUniqueId() <= 0) { 340 System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.6")); 341 version = 0; 342 } 343 } else if (ds.getVersion().equals("0.5")) { 344 if (version <= 0 && current.getUniqueId() > 0) { 345 System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5")); 346 version = 1; 347 } else if (version < 0 && current.getUniqueId() <= 0) { 348 System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.5")); 349 version = 0; 350 } 351 } else { 352 // should not happen. API version has been checked before 353 throwException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion())); 354 } 355 } else { 356 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 357 // 358 if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.6")) { 359 throwException(tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId()))); 360 } else if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) { 361 // default version in 0.5 files for existing primitives 362 System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5")); 363 version= 1; 364 } else if (current.getUniqueId() <= 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) { 365 // default version in 0.5 files for new primitives, no warning necessary. This is 366 // (was) legal in API 0.5 367 version= 0; 368 } 369 } 370 current.setVersion(version); 371 372 String action = atts.getValue("action"); 373 if (action == null) { 374 // do nothing 375 } else if (action.equals("delete")) { 376 current.setDeleted(true); 377 current.setModified(current.isVisible()); 378 } else if (action.equals("modify")) { 379 current.setModified(true); 380 } 381 382 String v = atts.getValue("changeset"); 383 if (v == null) { 384 current.setChangesetId(0); 385 } else { 386 try { 387 current.setChangesetId(Integer.parseInt(v)); 388 } catch(NumberFormatException e) { 389 if (current.getUniqueId() <= 0) { 390 // for a new primitive we just log a warning 391 System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId())); 392 current.setChangesetId(0); 393 } else { 394 // for an existing primitive this is a problem 395 throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 396 } 397 } 398 if (current.getChangesetId() <=0) { 399 if (current.getUniqueId() <= 0) { 400 // for a new primitive we just log a warning 401 System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId())); 402 current.setChangesetId(0); 403 } else { 404 // for an existing primitive this is a problem 405 throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 406 } 407 } 408 } 409 } 410 411 private long getLong(Attributes atts, String name) throws SAXException { 412 String value = atts.getValue(name); 413 if (value == null) { 414 throwException(tr("Missing required attribute ''{0}''.",name)); 415 } 416 try { 417 return Long.parseLong(value); 418 } catch(NumberFormatException e) { 419 throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.",name, value)); 420 } 421 return 0; // should not happen 422 } 423 } 424 425 /** 426 * Processes the ways after parsing. Rebuilds the list of nodes of each way and 427 * adds the way to the dataset 428 * 429 * @throws IllegalDataException thrown if a data integrity problem is detected 430 */ 431 protected void processWaysAfterParsing() throws IllegalDataException { 432 for (Long externalWayId: ways.keySet()) { 433 Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY)); 434 List<Node> wayNodes = new ArrayList<Node>(); 435 for (long id : ways.get(externalWayId)) { 436 Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE)); 437 if (n == null) { 438 if (id <= 0) 439 throw new IllegalDataException ( 440 tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.", 441 externalWayId, 442 id)); 443 // create an incomplete node if necessary 444 // 445 n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE); 446 if (n == null) { 447 n = new Node(id); 448 ds.addPrimitive(n); 449 } 450 } 451 if (n.isDeleted()) { 452 logger.warning(tr("Deleted node {0} is part of way {1}", id, w.getId())); 453 } else { 454 wayNodes.add(n); 455 } 456 } 457 w.setNodes(wayNodes); 458 if (w.hasIncompleteNodes()) { 459 if (logger.isLoggable(Level.FINE)) { 460 logger.fine(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.", 461 externalWayId, w.getNodesCount())); 462 } 463 } 464 ds.addPrimitive(w); 465 } 466 } 467 468 /** 469 * Processes the parsed nodes after parsing. Just adds them to 470 * the dataset 471 * 472 */ 473 protected void processNodesAfterParsing() { 474 for (OsmPrimitive primitive: externalIdMap.values()) { 475 if (primitive instanceof Node) { 476 this.ds.addPrimitive(primitive); 477 } 478 } 479 } 480 481 /** 482 * Completes the parsed relations with its members. 483 * 484 * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a 485 * relation member refers to a local primitive which wasn't available in the data 486 * 487 */ 488 private void processRelationsAfterParsing() throws IllegalDataException { 489 490 // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset 491 for (Long externalRelationId : relations.keySet()) { 492 Relation relation = (Relation) externalIdMap.get( 493 new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION) 494 ); 495 ds.addPrimitive(relation); 496 } 497 498 for (Long externalRelationId : relations.keySet()) { 499 Relation relation = (Relation) externalIdMap.get( 500 new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION) 501 ); 502 List<RelationMember> relationMembers = new ArrayList<RelationMember>(); 503 for (RelationMemberData rm : relations.get(externalRelationId)) { 504 OsmPrimitive primitive = null; 505 506 // lookup the member from the map of already created primitives 507 primitive = externalIdMap.get(new SimplePrimitiveId(rm.id, rm.type)); 508 509 if (primitive == null) { 510 if (rm.id <= 0) 511 // relation member refers to a primitive with a negative id which was not 512 // found in the data. This is always a data integrity problem and we abort 513 // with an exception 514 // 515 throw new IllegalDataException( 516 tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.", 517 externalRelationId, 518 rm.id)); 519 520 // member refers to OSM primitive which was not present in the parsed data 521 // -> create a new incomplete primitive and add it to the dataset 522 // 523 primitive = ds.getPrimitiveById(rm.id, rm.type); 524 if (primitive == null) { 525 switch (rm.type) { 526 case NODE: 527 primitive = new Node(rm.id); break; 528 case WAY: 529 primitive = new Way(rm.id); break; 530 case RELATION: 531 primitive = new Relation(rm.id); break; 532 default: throw new AssertionError(); // can't happen 533 } 534 535 ds.addPrimitive(primitive); 536 externalIdMap.put(new SimplePrimitiveId(rm.id, rm.type), primitive); 537 } 538 } 539 if (primitive.isDeleted()) { 540 logger.warning(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId())); 541 } else { 542 relationMembers.add(new RelationMember(rm.role, primitive)); 543 } 544 } 545 relation.setMembers(relationMembers); 546 } 547 } 18 public class MultiOsmReader extends OsmReader { 548 19 549 20 public void AddData(InputStream source) throws IllegalDataException { 550 21 try { 551 InputS ource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));552 SAXParserFactory.newInstance().newSAXParser().parse(inputSource, new Parser());553 } catch(ParserConfigurationException e) {554 throw new IllegalDataException(e.getMessage(), e);555 } catch( SAXException e) {556 throw new IllegalDataException(e.getMessage(),e);22 InputStreamReader ir = UTFInputStreamReader.create(source, "UTF-8"); 23 XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(ir); 24 setParser(parser); 25 parse(); 26 } catch(XMLStreamException e) { 27 throw new IllegalDataException(e); 557 28 } catch(Exception e) { 558 29 throw new IllegalDataException(e); … … 560 31 } 561 32 public void ProcessData() throws IllegalDataException { 562 processNodesAfterParsing(); 563 processWaysAfterParsing(); 564 processRelationsAfterParsing(); 33 prepareDataSet(); 565 34 } 566 567 568 35 } -
applications/editors/josm/plugins/reverter/src/reverter/OsmServerMultiObjectReader.java
r23273 r28820 14 14 import org.xml.sax.SAXException; 15 15 16 import reverter.corehacks.MultiOsmReader;17 16 18 17 public class OsmServerMultiObjectReader extends OsmServerReader {
Note:
See TracChangeset
for help on using the changeset viewer.