001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.data.osm;
003    
004    import java.util.ArrayList;
005    import java.util.Collection;
006    import java.util.HashSet;
007    import java.util.List;
008    import java.util.Set;
009    import java.util.concurrent.CopyOnWriteArrayList;
010    
011    import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
012    
013    /**
014     * This class allows to create and keep a deep copy of primitives. Provides methods to access directly added
015     * primitives and reference primitives
016     *
017     */
018    public class PrimitiveDeepCopy {
019    
020        public interface PasteBufferChangedListener {
021            void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer);
022        }
023    
024        private final List<PrimitiveData> directlyAdded = new ArrayList<PrimitiveData>();
025        private final List<PrimitiveData> referenced = new ArrayList<PrimitiveData>();
026        private final CopyOnWriteArrayList<PasteBufferChangedListener> listeners = new CopyOnWriteArrayList<PasteBufferChangedListener>();
027    
028        public PrimitiveDeepCopy() {
029    
030        }
031    
032        public PrimitiveDeepCopy(final Collection<OsmPrimitive> primitives) {
033            makeCopy(primitives);
034        }
035    
036        /**
037         * Replace content of the object with copy of provided primitives
038         * @param primitives
039         */
040        public final void makeCopy(final Collection<OsmPrimitive> primitives) {
041            directlyAdded.clear();
042            referenced.clear();
043    
044            final Set<Long> visitedNodeIds = new HashSet<Long>();
045            final Set<Long> visitedWayIds = new HashSet<Long>();
046            final Set<Long> visitedRelationIds = new HashSet<Long>();
047    
048            new AbstractVisitor() {
049                boolean firstIteration;
050    
051                public void visit(Node n) {
052                    if (!visitedNodeIds.add(n.getUniqueId()))
053                        return;
054                    (firstIteration ? directlyAdded : referenced).add(n.save());
055                }
056                public void visit(Way w) {
057                    if (!visitedWayIds.add(w.getUniqueId()))
058                        return;
059                    (firstIteration ? directlyAdded : referenced).add(w.save());
060                    firstIteration = false;
061                    for (Node n : w.getNodes()) {
062                        visit(n);
063                    }
064                }
065                public void visit(Relation r) {
066                    if (!visitedRelationIds.add(r.getUniqueId()))
067                        return;
068                    (firstIteration ? directlyAdded : referenced).add(r.save());
069                    firstIteration = false;
070                    for (RelationMember m : r.getMembers()) {
071                        m.getMember().visit(this);
072                    }
073                }
074    
075                public void visitAll() {
076                    for (OsmPrimitive osm : primitives) {
077                        firstIteration = true;
078                        osm.visit(this);
079                    }
080                }
081            }.visitAll();
082    
083            firePasteBufferChanged();
084        }
085    
086        public List<PrimitiveData> getDirectlyAdded() {
087            return directlyAdded;
088        }
089    
090        public List<PrimitiveData> getReferenced() {
091            return referenced;
092        }
093    
094        public List<PrimitiveData> getAll() {
095            List<PrimitiveData> result = new ArrayList<PrimitiveData>(directlyAdded.size() + referenced.size());
096            result.addAll(directlyAdded);
097            result.addAll(referenced);
098            return result;
099        }
100    
101        public boolean isEmpty() {
102            return directlyAdded.isEmpty() && referenced.isEmpty();
103        }
104    
105        private void firePasteBufferChanged() {
106            for (PasteBufferChangedListener listener: listeners) {
107                listener.pasteBufferChanged(this);
108            }
109        }
110    
111        public void addPasteBufferChangedListener(PasteBufferChangedListener listener) {
112            listeners.addIfAbsent(listener);
113        }
114    
115        public void removePasteBufferChangedListener(PasteBufferChangedListener listener) {
116            listeners.remove(listener);
117        }
118    
119    }