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 }