001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm.visitor; 003 004import java.util.ArrayList; 005import java.util.HashMap; 006import java.util.List; 007import java.util.Map; 008 009import org.openstreetmap.josm.data.osm.DataSet; 010import org.openstreetmap.josm.data.osm.Node; 011import org.openstreetmap.josm.data.osm.NodeData; 012import org.openstreetmap.josm.data.osm.OsmPrimitive; 013import org.openstreetmap.josm.data.osm.PrimitiveData; 014import org.openstreetmap.josm.data.osm.Relation; 015import org.openstreetmap.josm.data.osm.RelationData; 016import org.openstreetmap.josm.data.osm.RelationMember; 017import org.openstreetmap.josm.data.osm.RelationMemberData; 018import org.openstreetmap.josm.data.osm.Way; 019import org.openstreetmap.josm.data.osm.WayData; 020import org.openstreetmap.josm.tools.CheckParameterUtil; 021 022/** 023 * MergeSourceBuildingVisitor helps to build the "hull" of a collection of {@link OsmPrimitive}s 024 * which shall be merged into another layer. The "hull" is slightly bigger than the original 025 * collection. It includes, for instance the nodes of a way in the original collection even though 026 * these nodes might not be present explicitly in the original collection. The "hull" also includes 027 * incomplete {@link OsmPrimitive}s which are referred to by relations in the original collection. And 028 * it turns {@link OsmPrimitive} referred to by {@link Relation}s in the original collection into 029 * incomplete {@link OsmPrimitive}s in the "hull", if they are not themselves present in the 030 * original collection. 031 * 032 */ 033public class MergeSourceBuildingVisitor extends AbstractVisitor { 034 private DataSet selectionBase; 035 private DataSet hull; 036 private Map<OsmPrimitive, PrimitiveData> mappedPrimitives; 037 038 /** 039 * Creates the visitor. The visitor starts to build the "hull" from 040 * the currently selected primitives in the dataset <code>selectionBase</code>, 041 * i.e. from {@link DataSet#getSelected()}. 042 * 043 * @param selectionBase the dataset. Must not be null. 044 * @exception IllegalArgumentException thrown if selectionBase is null 045 * 046 */ 047 public MergeSourceBuildingVisitor(DataSet selectionBase) throws IllegalArgumentException { 048 CheckParameterUtil.ensureParameterNotNull(selectionBase, "selectionBase"); 049 this.selectionBase = selectionBase; 050 this.hull = new DataSet(); 051 this.mappedPrimitives = new HashMap<OsmPrimitive, PrimitiveData>(); 052 } 053 054 protected boolean isInSelectionBase(OsmPrimitive primitive) { 055 return selectionBase.getAllSelected().contains(primitive); 056 } 057 058 protected boolean isAlreadyRemembered(OsmPrimitive primitive) { 059 return mappedPrimitives.keySet().contains(primitive); 060 } 061 062 /** 063 * Remebers a node in the "hull" 064 * 065 * @param n the node 066 */ 067 protected void rememberNode(Node n) { 068 if (isAlreadyRemembered(n)) 069 return; 070 mappedPrimitives.put(n, n.save()); 071 } 072 073 /** 074 * remembers a way in the hull 075 * 076 * @param w the way 077 */ 078 protected void rememberWay(Way w) { 079 if (isAlreadyRemembered(w)) 080 return; 081 WayData clone = w.save(); 082 List<Long> newNodes = new ArrayList<Long>(w.getNodesCount()); 083 for (Node n: w.getNodes()) { 084 newNodes.add(mappedPrimitives.get(n).getUniqueId()); 085 } 086 clone.setNodes(newNodes); 087 mappedPrimitives.put(w, clone); 088 } 089 090 /** 091 * Remembers a relation in the hull 092 * 093 * @param r the relation 094 */ 095 protected void rememberRelation(Relation r) { 096 RelationData clone; 097 if (isAlreadyRemembered(r)) { 098 clone = (RelationData)mappedPrimitives.get(r); 099 } else { 100 clone = r.save(); 101 mappedPrimitives.put(r, clone); 102 } 103 104 List<RelationMemberData> newMembers = new ArrayList<RelationMemberData>(); 105 for (RelationMember member: r.getMembers()) { 106 newMembers.add( 107 new RelationMemberData(member.getRole(), mappedPrimitives.get(member.getMember()))); 108 109 } 110 clone.setMembers(newMembers); 111 } 112 113 protected void rememberRelationPartial(Relation r) { 114 if (isAlreadyRemembered(r)) 115 return; 116 RelationData clone = r.save(); 117 clone.getMembers().clear(); 118 mappedPrimitives.put(r, clone); 119 } 120 121 protected void rememberIncomplete(OsmPrimitive primitive) { 122 if (isAlreadyRemembered(primitive)) 123 return; 124 PrimitiveData clone = primitive.save(); 125 clone.setIncomplete(true); 126 mappedPrimitives.put(primitive, clone); 127 } 128 129 @Override 130 public void visit(Node n) { 131 rememberNode(n); 132 } 133 134 @Override 135 public void visit(Way w) { 136 // remember all nodes this way refers to ... 137 // 138 for (Node n: w.getNodes()) { 139 n.accept(this); 140 } 141 // ... and the way itself 142 rememberWay(w); 143 } 144 145 @Override 146 public void visit(Relation r) { 147 // first, remember all primitives members refer to (only if necessary, see 148 // below) 149 // 150 rememberRelationPartial(r); 151 for (RelationMember member: r.getMembers()) { 152 if (isAlreadyRemembered(member.getMember())) { 153 // referred primitive already remembered 154 // 155 continue; 156 } 157 if (isInSelectionBase(member.getMember()) || member.getMember().isNew()) { 158 member.getMember().accept(this); 159 } else { 160 rememberIncomplete(member.getMember()); 161 } 162 } 163 rememberRelation(r); 164 } 165 166 protected void buildHull() { 167 // Create all primitives first 168 for (PrimitiveData primitive: mappedPrimitives.values()) { 169 OsmPrimitive newPrimitive = hull.getPrimitiveById(primitive); 170 boolean created = newPrimitive == null; 171 if (created) { 172 newPrimitive = primitive.getType().newInstance(primitive.getUniqueId(), true); 173 } 174 if (newPrimitive instanceof Node && !primitive.isIncomplete()) { 175 newPrimitive.load(primitive); 176 } 177 if (created) { 178 hull.addPrimitive(newPrimitive); 179 } 180 } 181 // Then ways and relations 182 for (PrimitiveData primitive : mappedPrimitives.values()) { 183 if (!(primitive instanceof NodeData) && !primitive.isIncomplete()) { 184 hull.getPrimitiveById(primitive).load(primitive); 185 } 186 } 187 } 188 189 public DataSet build() { 190 for (OsmPrimitive primitive: selectionBase.getAllSelected()) { 191 primitive.accept(this); 192 } 193 buildHull(); 194 return hull; 195 } 196}