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    }