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 original collection. 030 * @since 1891 031 */ 032public class MergeSourceBuildingVisitor implements OsmPrimitiveVisitor { 033 private final DataSet selectionBase; 034 private final DataSet hull; 035 private final Map<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 * @throws IllegalArgumentException if selectionBase is null 044 */ 045 public MergeSourceBuildingVisitor(DataSet selectionBase) { 046 CheckParameterUtil.ensureParameterNotNull(selectionBase, "selectionBase"); 047 this.selectionBase = selectionBase; 048 this.hull = new DataSet(); 049 this.mappedPrimitives = new HashMap<>(); 050 } 051 052 protected boolean isInSelectionBase(OsmPrimitive primitive) { 053 return selectionBase.getAllSelected().contains(primitive); 054 } 055 056 protected boolean isAlreadyRemembered(OsmPrimitive primitive) { 057 return mappedPrimitives.containsKey(primitive); 058 } 059 060 /** 061 * Remebers a node in the "hull" 062 * 063 * @param n the node 064 */ 065 protected void rememberNode(Node n) { 066 if (isAlreadyRemembered(n)) 067 return; 068 mappedPrimitives.put(n, n.save()); 069 } 070 071 /** 072 * remembers a way in the hull 073 * 074 * @param w the way 075 */ 076 protected void rememberWay(Way w) { 077 if (isAlreadyRemembered(w)) 078 return; 079 WayData clone = w.save(); 080 List<Long> newNodes = new ArrayList<>(w.getNodesCount()); 081 for (Node n: w.getNodes()) { 082 newNodes.add(mappedPrimitives.get(n).getUniqueId()); 083 } 084 clone.setNodeIds(newNodes); 085 mappedPrimitives.put(w, clone); 086 } 087 088 /** 089 * Remembers a relation in the hull 090 * 091 * @param r the relation 092 */ 093 protected void rememberRelation(Relation r) { 094 RelationData clone; 095 if (isAlreadyRemembered(r)) { 096 clone = (RelationData) mappedPrimitives.get(r); 097 } else { 098 clone = r.save(); 099 mappedPrimitives.put(r, clone); 100 } 101 102 List<RelationMemberData> newMembers = new ArrayList<>(); 103 for (RelationMember member: r.getMembers()) { 104 newMembers.add(new RelationMemberData(member.getRole(), mappedPrimitives.get(member.getMember()))); 105 106 } 107 clone.setMembers(newMembers); 108 } 109 110 protected void rememberRelationPartial(Relation r) { 111 if (isAlreadyRemembered(r)) 112 return; 113 RelationData clone = r.save(); 114 clone.getMembers().clear(); 115 mappedPrimitives.put(r, clone); 116 } 117 118 protected void rememberIncomplete(OsmPrimitive primitive) { 119 if (isAlreadyRemembered(primitive)) 120 return; 121 PrimitiveData clone = primitive.save(); 122 clone.setIncomplete(true); 123 mappedPrimitives.put(primitive, clone); 124 } 125 126 @Override 127 public void visit(Node n) { 128 rememberNode(n); 129 } 130 131 @Override 132 public void visit(Way w) { 133 // remember all nodes this way refers to ... 134 for (Node n: w.getNodes()) { 135 n.accept(this); 136 } 137 // ... and the way itself 138 rememberWay(w); 139 } 140 141 @Override 142 public void visit(Relation r) { 143 // first, remember all primitives members refer to (only if necessary, see below) 144 rememberRelationPartial(r); 145 for (RelationMember member: r.getMembers()) { 146 if (isAlreadyRemembered(member.getMember())) { 147 // referred primitive already remembered 148 continue; 149 } 150 if (isInSelectionBase(member.getMember()) || member.getMember().isNew()) { 151 member.getMember().accept(this); 152 } else { 153 rememberIncomplete(member.getMember()); 154 } 155 } 156 rememberRelation(r); 157 } 158 159 protected void buildHull() { 160 // Create all primitives first 161 for (PrimitiveData primitive: mappedPrimitives.values()) { 162 OsmPrimitive newPrimitive = hull.getPrimitiveById(primitive); 163 boolean created = newPrimitive == null; 164 if (created) { 165 newPrimitive = primitive.getType().newInstance(primitive.getUniqueId(), true); 166 } 167 if (newPrimitive instanceof Node && !primitive.isIncomplete()) { 168 newPrimitive.load(primitive); 169 } 170 if (created) { 171 hull.addPrimitive(newPrimitive); 172 } 173 } 174 // Then ways and relations 175 for (PrimitiveData primitive : mappedPrimitives.values()) { 176 if (!(primitive instanceof NodeData) && !primitive.isIncomplete()) { 177 hull.getPrimitiveById(primitive).load(primitive); 178 } 179 } 180 } 181 182 /** 183 * Builds and returns the "hull". 184 * @return the "hull" data set 185 */ 186 public DataSet build() { 187 for (OsmPrimitive primitive: selectionBase.getAllSelected()) { 188 primitive.accept(this); 189 } 190 buildHull(); 191 return hull; 192 } 193}