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