001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Set; 009import java.util.concurrent.CopyOnWriteArrayList; 010 011import 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 */ 018public class PrimitiveDeepCopy { 019 020 public interface PasteBufferChangedListener { 021 void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer); 022 } 023 024 private final List<PrimitiveData> directlyAdded = new ArrayList<>(); 025 private final List<PrimitiveData> referenced = new ArrayList<>(); 026 private final CopyOnWriteArrayList<PasteBufferChangedListener> listeners = new CopyOnWriteArrayList<>(); 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<>(); 045 final Set<Long> visitedWayIds = new HashSet<>(); 046 final Set<Long> visitedRelationIds = new HashSet<>(); 047 048 new AbstractVisitor() { 049 boolean firstIteration; 050 051 @Override 052 public void visit(Node n) { 053 if (!visitedNodeIds.add(n.getUniqueId())) 054 return; 055 (firstIteration ? directlyAdded : referenced).add(n.save()); 056 } 057 @Override 058 public void visit(Way w) { 059 if (!visitedWayIds.add(w.getUniqueId())) 060 return; 061 (firstIteration ? directlyAdded : referenced).add(w.save()); 062 firstIteration = false; 063 for (Node n : w.getNodes()) { 064 visit(n); 065 } 066 } 067 @Override 068 public void visit(Relation r) { 069 if (!visitedRelationIds.add(r.getUniqueId())) 070 return; 071 (firstIteration ? directlyAdded : referenced).add(r.save()); 072 firstIteration = false; 073 for (RelationMember m : r.getMembers()) { 074 m.getMember().accept(this); 075 } 076 } 077 078 public final void visitAll() { 079 for (OsmPrimitive osm : primitives) { 080 firstIteration = true; 081 osm.accept(this); 082 } 083 } 084 }.visitAll(); 085 086 firePasteBufferChanged(); 087 } 088 089 public List<PrimitiveData> getDirectlyAdded() { 090 return directlyAdded; 091 } 092 093 public List<PrimitiveData> getReferenced() { 094 return referenced; 095 } 096 097 public List<PrimitiveData> getAll() { 098 List<PrimitiveData> result = new ArrayList<>(directlyAdded.size() + referenced.size()); 099 result.addAll(directlyAdded); 100 result.addAll(referenced); 101 return result; 102 } 103 104 public boolean isEmpty() { 105 return directlyAdded.isEmpty() && referenced.isEmpty(); 106 } 107 108 private void firePasteBufferChanged() { 109 for (PasteBufferChangedListener listener: listeners) { 110 listener.pasteBufferChanged(this); 111 } 112 } 113 114 public void addPasteBufferChangedListener(PasteBufferChangedListener listener) { 115 listeners.addIfAbsent(listener); 116 } 117 118 public void removePasteBufferChangedListener(PasteBufferChangedListener listener) { 119 listeners.remove(listener); 120 } 121 122}