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 }