001 // License: GPL. See LICENSE file for details. 002 package org.openstreetmap.josm.io; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.util.ArrayList; 007 import java.util.Collection; 008 import java.util.HashMap; 009 import java.util.List; 010 import java.util.Map; 011 012 import org.openstreetmap.josm.data.osm.Changeset; 013 import org.openstreetmap.josm.data.osm.DataSet; 014 import org.openstreetmap.josm.data.osm.Node; 015 import org.openstreetmap.josm.data.osm.OsmPrimitive; 016 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 017 import org.openstreetmap.josm.data.osm.PrimitiveId; 018 import org.openstreetmap.josm.data.osm.Relation; 019 import org.openstreetmap.josm.data.osm.RelationMember; 020 import org.openstreetmap.josm.data.osm.RelationMemberData; 021 import org.openstreetmap.josm.data.osm.SimplePrimitiveId; 022 import org.openstreetmap.josm.data.osm.Way; 023 024 /** 025 * Abstract Reader, allowing other implementations than OsmReader (PbfReader in PBF plugin for example) 026 * @author Vincent 027 * 028 */ 029 public abstract class AbstractReader { 030 031 /** 032 * The dataset to add parsed objects to. 033 */ 034 protected DataSet ds = new DataSet(); 035 036 protected Changeset uploadChangeset; 037 038 /** the map from external ids to read OsmPrimitives. External ids are 039 * longs too, but in contrast to internal ids negative values are used 040 * to identify primitives unknown to the OSM server 041 */ 042 protected final Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 043 044 /** 045 * Data structure for the remaining way objects 046 */ 047 protected final Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>(); 048 049 /** 050 * Data structure for relation objects 051 */ 052 protected final Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>(); 053 054 /** 055 * Replies the parsed data set 056 * 057 * @return the parsed data set 058 */ 059 public DataSet getDataSet() { 060 return ds; 061 } 062 063 /** 064 * Processes the parsed nodes after parsing. Just adds them to 065 * the dataset 066 * 067 */ 068 protected void processNodesAfterParsing() { 069 for (OsmPrimitive primitive: externalIdMap.values()) { 070 if (primitive instanceof Node) { 071 this.ds.addPrimitive(primitive); 072 } 073 } 074 } 075 076 /** 077 * Processes the ways after parsing. Rebuilds the list of nodes of each way and 078 * adds the way to the dataset 079 * 080 * @throws IllegalDataException thrown if a data integrity problem is detected 081 */ 082 protected void processWaysAfterParsing() throws IllegalDataException{ 083 for (Long externalWayId: ways.keySet()) { 084 Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY)); 085 List<Node> wayNodes = new ArrayList<Node>(); 086 for (long id : ways.get(externalWayId)) { 087 Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE)); 088 if (n == null) { 089 if (id <= 0) 090 throw new IllegalDataException ( 091 tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.", 092 externalWayId, 093 id)); 094 // create an incomplete node if necessary 095 // 096 n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE); 097 if (n == null) { 098 n = new Node(id); 099 ds.addPrimitive(n); 100 } 101 } 102 if (n.isDeleted()) { 103 System.out.println(tr("Deleted node {0} is part of way {1}", id, w.getId())); 104 } else { 105 wayNodes.add(n); 106 } 107 } 108 w.setNodes(wayNodes); 109 if (w.hasIncompleteNodes()) { 110 System.out.println(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.", 111 externalWayId, w.getNodesCount())); 112 } 113 ds.addPrimitive(w); 114 } 115 } 116 117 /** 118 * Completes the parsed relations with its members. 119 * 120 * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a 121 * relation member refers to a local primitive which wasn't available in the data 122 * 123 */ 124 protected void processRelationsAfterParsing() throws IllegalDataException { 125 126 // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset 127 for (Long externalRelationId : relations.keySet()) { 128 Relation relation = (Relation) externalIdMap.get( 129 new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION) 130 ); 131 ds.addPrimitive(relation); 132 } 133 134 for (Long externalRelationId : relations.keySet()) { 135 Relation relation = (Relation) externalIdMap.get( 136 new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION) 137 ); 138 List<RelationMember> relationMembers = new ArrayList<RelationMember>(); 139 for (RelationMemberData rm : relations.get(externalRelationId)) { 140 OsmPrimitive primitive = null; 141 142 // lookup the member from the map of already created primitives 143 primitive = externalIdMap.get(new SimplePrimitiveId(rm.getMemberId(), rm.getMemberType())); 144 145 if (primitive == null) { 146 if (rm.getMemberId() <= 0) 147 // relation member refers to a primitive with a negative id which was not 148 // found in the data. This is always a data integrity problem and we abort 149 // with an exception 150 // 151 throw new IllegalDataException( 152 tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.", 153 externalRelationId, 154 rm.getMemberId())); 155 156 // member refers to OSM primitive which was not present in the parsed data 157 // -> create a new incomplete primitive and add it to the dataset 158 // 159 primitive = ds.getPrimitiveById(rm.getMemberId(), rm.getMemberType()); 160 if (primitive == null) { 161 switch (rm.getMemberType()) { 162 case NODE: 163 primitive = new Node(rm.getMemberId()); break; 164 case WAY: 165 primitive = new Way(rm.getMemberId()); break; 166 case RELATION: 167 primitive = new Relation(rm.getMemberId()); break; 168 default: throw new AssertionError(); // can't happen 169 } 170 171 ds.addPrimitive(primitive); 172 externalIdMap.put(new SimplePrimitiveId(rm.getMemberId(), rm.getMemberType()), primitive); 173 } 174 } 175 if (primitive.isDeleted()) { 176 System.out.println(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId())); 177 } else { 178 relationMembers.add(new RelationMember(rm.getRole(), primitive)); 179 } 180 } 181 relation.setMembers(relationMembers); 182 } 183 } 184 185 protected void processChangesetAfterParsing() { 186 if (uploadChangeset != null) { 187 for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) { 188 ds.addChangeSetTag(e.getKey(), e.getValue()); 189 } 190 } 191 } 192 193 protected final void prepareDataSet() throws IllegalDataException { 194 try { 195 ds.beginUpdate(); 196 processNodesAfterParsing(); 197 processWaysAfterParsing(); 198 processRelationsAfterParsing(); 199 processChangesetAfterParsing(); 200 } finally { 201 ds.endUpdate(); 202 } 203 } 204 }