1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.gui.datatransfer.data;
|
---|
3 |
|
---|
4 | import java.awt.datatransfer.DataFlavor;
|
---|
5 | import java.io.Serializable;
|
---|
6 | import java.util.ArrayList;
|
---|
7 | import java.util.Collection;
|
---|
8 | import java.util.Collections;
|
---|
9 | import java.util.LinkedHashSet;
|
---|
10 | import java.util.LinkedList;
|
---|
11 | import java.util.Queue;
|
---|
12 | import java.util.Set;
|
---|
13 |
|
---|
14 | import org.openstreetmap.josm.data.ProjectionBounds;
|
---|
15 | import org.openstreetmap.josm.data.coor.EastNorth;
|
---|
16 | import org.openstreetmap.josm.data.osm.NodeData;
|
---|
17 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
18 | import org.openstreetmap.josm.data.osm.PrimitiveData;
|
---|
19 | import org.openstreetmap.josm.data.osm.Relation;
|
---|
20 | import org.openstreetmap.josm.data.osm.Way;
|
---|
21 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
|
---|
22 | import org.openstreetmap.josm.tools.CompositeList;
|
---|
23 |
|
---|
24 | /**
|
---|
25 | * A list of primitives that are transferred. The list allows you to implicitly add primitives.
|
---|
26 | * It distinguishes between primitives that were directly added and implicitly added ones.
|
---|
27 | * @author Michael Zangl
|
---|
28 | * @since 10604
|
---|
29 | */
|
---|
30 | public final class PrimitiveTransferData implements Serializable {
|
---|
31 | private static final long serialVersionUID = 1L;
|
---|
32 |
|
---|
33 | /**
|
---|
34 | * The data flavor used to represent this class.
|
---|
35 | */
|
---|
36 | public static final DataFlavor DATA_FLAVOR = new DataFlavor(PrimitiveTransferData.class, "OSM Primitives");
|
---|
37 |
|
---|
38 | private static final class GetReferences implements ReferenceGetter {
|
---|
39 | @Override
|
---|
40 | public Collection<? extends OsmPrimitive> getReferredPrimitives(OsmPrimitive primitive) {
|
---|
41 | if (primitive instanceof Way) {
|
---|
42 | return ((Way) primitive).getNodes();
|
---|
43 | } else if (primitive instanceof Relation) {
|
---|
44 | return ((Relation) primitive).getMemberPrimitivesList();
|
---|
45 | } else {
|
---|
46 | return Collections.emptyList();
|
---|
47 | }
|
---|
48 | }
|
---|
49 | }
|
---|
50 |
|
---|
51 | @FunctionalInterface
|
---|
52 | private interface ReferenceGetter {
|
---|
53 | Collection<? extends OsmPrimitive> getReferredPrimitives(OsmPrimitive primitive);
|
---|
54 | }
|
---|
55 |
|
---|
56 | private final ArrayList<PrimitiveData> direct;
|
---|
57 | private final ArrayList<PrimitiveData> referenced;
|
---|
58 |
|
---|
59 | /**
|
---|
60 | * Create the new transfer data.
|
---|
61 | * @param primitives The primitives to transfer
|
---|
62 | * @param referencedGetter A function that allows to get the primitives referenced by the primitives variable.
|
---|
63 | * It will be queried recursively.
|
---|
64 | */
|
---|
65 | private PrimitiveTransferData(Collection<? extends OsmPrimitive> primitives, ReferenceGetter referencedGetter) {
|
---|
66 | // convert to hash set first to remove duplicates
|
---|
67 | Set<OsmPrimitive> visited = new LinkedHashSet<>(primitives);
|
---|
68 | this.direct = new ArrayList<>(visited.size());
|
---|
69 |
|
---|
70 | this.referenced = new ArrayList<>();
|
---|
71 | Queue<OsmPrimitive> toCheck = new LinkedList<>();
|
---|
72 | for (OsmPrimitive p : visited) {
|
---|
73 | direct.add(p.save());
|
---|
74 | toCheck.addAll(referencedGetter.getReferredPrimitives(p));
|
---|
75 | }
|
---|
76 | while (!toCheck.isEmpty()) {
|
---|
77 | OsmPrimitive p = toCheck.poll();
|
---|
78 | if (visited.add(p)) {
|
---|
79 | referenced.add(p.save());
|
---|
80 | toCheck.addAll(referencedGetter.getReferredPrimitives(p));
|
---|
81 | }
|
---|
82 | }
|
---|
83 | }
|
---|
84 |
|
---|
85 | /**
|
---|
86 | * Gets all primitives directly added.
|
---|
87 | * @return The primitives
|
---|
88 | */
|
---|
89 | public Collection<PrimitiveData> getDirectlyAdded() {
|
---|
90 | return Collections.unmodifiableList(direct);
|
---|
91 | }
|
---|
92 |
|
---|
93 | /**
|
---|
94 | * Gets all primitives that were added because they were referenced.
|
---|
95 | * @return The primitives
|
---|
96 | */
|
---|
97 | public Collection<PrimitiveData> getReferenced() {
|
---|
98 | return Collections.unmodifiableList(referenced);
|
---|
99 | }
|
---|
100 |
|
---|
101 | /**
|
---|
102 | * Gets a List of all primitives added to this set.
|
---|
103 | * @return That list.
|
---|
104 | */
|
---|
105 | public Collection<PrimitiveData> getAll() {
|
---|
106 | return new CompositeList<>(direct, referenced);
|
---|
107 | }
|
---|
108 |
|
---|
109 | /**
|
---|
110 | * Creates a new {@link PrimitiveTransferData} object that only contains the primitives.
|
---|
111 | * @param primitives The primitives to contain.
|
---|
112 | * @return That set.
|
---|
113 | */
|
---|
114 | public static PrimitiveTransferData getData(Collection<? extends OsmPrimitive> primitives) {
|
---|
115 | return new PrimitiveTransferData(primitives, primitive -> Collections.emptyList());
|
---|
116 | }
|
---|
117 |
|
---|
118 | /**
|
---|
119 | * Creates a new {@link PrimitiveTransferData} object that contains the primitives and all references.
|
---|
120 | * @param primitives The primitives to contain.
|
---|
121 | * @return That set.
|
---|
122 | */
|
---|
123 | public static PrimitiveTransferData getDataWithReferences(Collection<? extends OsmPrimitive> primitives) {
|
---|
124 | return new PrimitiveTransferData(primitives, new GetReferences());
|
---|
125 | }
|
---|
126 |
|
---|
127 | /**
|
---|
128 | * Compute the center of all nodes.
|
---|
129 | * @return The center or null if this buffer has no location.
|
---|
130 | */
|
---|
131 | public EastNorth getCenter() {
|
---|
132 | BoundingXYVisitor visitor = new BoundingXYVisitor();
|
---|
133 | for (PrimitiveData pd : getAll()) {
|
---|
134 | if (pd instanceof NodeData && !pd.isIncomplete()) {
|
---|
135 | visitor.visit(((NodeData) pd));
|
---|
136 | }
|
---|
137 | }
|
---|
138 | ProjectionBounds bounds = visitor.getBounds();
|
---|
139 | if (bounds == null) {
|
---|
140 | return null;
|
---|
141 | } else {
|
---|
142 | return bounds.getCenter();
|
---|
143 | }
|
---|
144 | }
|
---|
145 |
|
---|
146 | /**
|
---|
147 | * Tests whether this set contains any primitives that have invalid data.
|
---|
148 | * @return <code>true</code> if invalid data is contained in this set.
|
---|
149 | */
|
---|
150 | public boolean hasIncompleteData() {
|
---|
151 | return getAll().stream().anyMatch(p -> !p.isUsable());
|
---|
152 | }
|
---|
153 | }
|
---|