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