source: josm/trunk/src/org/openstreetmap/josm/data/APIDataSet.java@ 8851

Last change on this file since 8851 was 8851, checked in by Don-vip, 9 years ago

sonar - squid:S1149 - Synchronized classes Vector, Hashtable, Stack and StringBuffer should not be used

  • Property svn:eol-style set to native
File size: 12.1 KB
RevLine 
[2512]1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data;
3
[8851]4import java.util.ArrayDeque;
[2512]5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.Comparator;
[8851]9import java.util.Deque;
[2512]10import java.util.HashMap;
11import java.util.HashSet;
12import java.util.LinkedList;
13import java.util.List;
[6317]14import java.util.Map;
[2512]15import java.util.Set;
16
[2598]17import org.openstreetmap.josm.actions.upload.CyclicUploadDependencyException;
[2979]18import org.openstreetmap.josm.data.conflict.Conflict;
19import org.openstreetmap.josm.data.conflict.ConflictCollection;
[2512]20import org.openstreetmap.josm.data.osm.DataSet;
[4534]21import org.openstreetmap.josm.data.osm.IPrimitive;
[2512]22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
[4113]24import org.openstreetmap.josm.data.osm.OsmPrimitiveComparator;
[2979]25import org.openstreetmap.josm.data.osm.PrimitiveId;
[2512]26import org.openstreetmap.josm.data.osm.Relation;
27import org.openstreetmap.josm.data.osm.RelationMember;
28import org.openstreetmap.josm.data.osm.Way;
[4100]29import org.openstreetmap.josm.tools.Utils;
[2512]30
31/**
[7599]32 * Represents a collection of {@link OsmPrimitive}s which should be uploaded to the API.
[5266]33 * The collection is derived from the modified primitives of an {@link DataSet} and it provides methods
[2598]34 * for sorting the objects in upload order.
[7599]35 * @since 2025
[2512]36 */
37public class APIDataSet {
[6317]38 private List<OsmPrimitive> toAdd;
39 private List<OsmPrimitive> toUpdate;
40 private List<OsmPrimitive> toDelete;
[2512]41
42 /**
43 * creates a new empty data set
44 */
45 public APIDataSet() {
[7005]46 toAdd = new LinkedList<>();
47 toUpdate = new LinkedList<>();
48 toDelete = new LinkedList<>();
[2512]49 }
50
51 /**
52 * initializes the API data set with the modified primitives in <code>ds</code>
53 *
54 * @param ds the data set. Ignored, if null.
55 */
56 public void init(DataSet ds) {
57 if (ds == null) return;
[5589]58 init(ds.allPrimitives());
59 }
60
[6890]61 public final void init(Collection<OsmPrimitive> primitives) {
[2512]62 toAdd.clear();
63 toUpdate.clear();
64 toDelete.clear();
65
[5589]66 for (OsmPrimitive osm :primitives) {
[3336]67 if (osm.isNewOrUndeleted() && !osm.isDeleted()) {
[2512]68 toAdd.add(osm);
69 } else if (osm.isModified() && !osm.isDeleted()) {
70 toUpdate.add(osm);
[3336]71 } else if (osm.isDeleted() && !osm.isNew() && osm.isModified() && osm.isVisible()) {
[2512]72 toDelete.add(osm);
73 }
74 }
[7120]75 OsmPrimitiveComparator c = new OsmPrimitiveComparator(false, true);
[4874]76 Collections.sort(toDelete, c);
77 Collections.sort(toAdd, c);
78 Collections.sort(toUpdate, c);
[2512]79 }
80
81 /**
82 * initializes the API data set with the modified primitives in <code>ds</code>
83 *
84 * @param ds the data set. Ignored, if null.
85 */
86 public APIDataSet(DataSet ds) {
87 this();
88 init(ds);
89 }
90
91 /**
[2979]92 * Replies true if one of the primitives to be updated or to be deleted
93 * participates in the conflict <code>conflict</code>
[3530]94 *
[2979]95 * @param conflict the conflict
96 * @return true if one of the primitives to be updated or to be deleted
97 * participates in the conflict <code>conflict</code>
98 */
99 public boolean participatesInConflict(Conflict<?> conflict) {
100 if (conflict == null) return false;
101 for (OsmPrimitive p: toUpdate) {
102 if (conflict.isParticipating(p)) return true;
103 }
104 for (OsmPrimitive p: toDelete) {
105 if (conflict.isParticipating(p)) return true;
106 }
107 return false;
108 }
109
110 /**
111 * Replies true if one of the primitives to be updated or to be deleted
112 * participates in at least one conflict in <code>conflicts</code>
[3530]113 *
[2979]114 * @param conflicts the collection of conflicts
115 * @return true if one of the primitives to be updated or to be deleted
116 * participates in at least one conflict in <code>conflicts</code>
117 */
118 public boolean participatesInConflict(ConflictCollection conflicts) {
119 if (conflicts == null || conflicts.isEmpty()) return false;
[7005]120 Set<PrimitiveId> idsParticipatingInConflicts = new HashSet<>();
[2979]121 for (OsmPrimitive p: conflicts.getMyConflictParties()) {
122 idsParticipatingInConflicts.add(p.getPrimitiveId());
123 }
124 for (OsmPrimitive p: conflicts.getTheirConflictParties()) {
125 idsParticipatingInConflicts.add(p.getPrimitiveId());
126 }
127 for (OsmPrimitive p: toUpdate) {
128 if (idsParticipatingInConflicts.contains(p.getPrimitiveId())) return true;
129 }
130 for (OsmPrimitive p: toDelete) {
131 if (idsParticipatingInConflicts.contains(p.getPrimitiveId())) return true;
132 }
133 return false;
134 }
135
136 /**
[2512]137 * initializes the API data set with the primitives in <code>primitives</code>
138 *
139 * @param primitives the collection of primitives
140 */
141 public APIDataSet(Collection<OsmPrimitive> primitives) {
142 this();
[5589]143 init(primitives);
[2512]144 }
145
146 /**
147 * Replies true if there are no primitives to upload
148 *
149 * @return true if there are no primitives to upload
150 */
151 public boolean isEmpty() {
152 return toAdd.isEmpty() && toUpdate.isEmpty() && toDelete.isEmpty();
153 }
154
155 /**
156 * Replies the primitives which should be added to the OSM database
157 *
158 * @return the primitives which should be added to the OSM database
159 */
160 public List<OsmPrimitive> getPrimitivesToAdd() {
161 return toAdd;
162 }
163
164 /**
165 * Replies the primitives which should be updated in the OSM database
166 *
167 * @return the primitives which should be updated in the OSM database
168 */
169 public List<OsmPrimitive> getPrimitivesToUpdate() {
170 return toUpdate;
171 }
172
173 /**
174 * Replies the primitives which should be deleted in the OSM database
175 *
176 * @return the primitives which should be deleted in the OSM database
177 */
178 public List<OsmPrimitive> getPrimitivesToDelete() {
179 return toDelete;
180 }
181
182 /**
183 * Replies all primitives
184 *
185 * @return all primitives
186 */
187 public List<OsmPrimitive> getPrimitives() {
[8338]188 List<OsmPrimitive> ret = new LinkedList<>();
[2512]189 ret.addAll(toAdd);
190 ret.addAll(toUpdate);
191 ret.addAll(toDelete);
192 return ret;
193 }
194
195 /**
[2598]196 * Replies the number of objects to upload
[2711]197 *
[2598]198 * @return the number of objects to upload
199 */
200 public int getSize() {
201 return toAdd.size() + toUpdate.size() + toDelete.size();
202 }
203
[4534]204 public void removeProcessed(Collection<IPrimitive> processed) {
[2598]205 if (processed == null) return;
206 toAdd.removeAll(processed);
207 toUpdate.removeAll(processed);
208 toDelete.removeAll(processed);
209 }
210
211 /**
[2512]212 * Adjusts the upload order for new relations. Child relations are uploaded first,
213 * parent relations second.
214 *
215 * This method detects cyclic dependencies in new relation. Relations with cyclic
216 * dependencies can't be uploaded.
217 *
[8291]218 * @throws CyclicUploadDependencyException if a cyclic dependency is detected
[2512]219 */
[8510]220 public void adjustRelationUploadOrder() throws CyclicUploadDependencyException {
[8338]221 List<OsmPrimitive> newToAdd = new LinkedList<>();
[4100]222 newToAdd.addAll(Utils.filteredCollection(toAdd, Node.class));
223 newToAdd.addAll(Utils.filteredCollection(toAdd, Way.class));
[2512]224
[7005]225 List<Relation> relationsToAdd = new ArrayList<>(Utils.filteredCollection(toAdd, Relation.class));
[2512]226 List<Relation> noProblemRelations = filterRelationsNotReferringToNewRelations(relationsToAdd);
227 newToAdd.addAll(noProblemRelations);
228 relationsToAdd.removeAll(noProblemRelations);
229
[6776]230 RelationUploadDependencyGraph graph = new RelationUploadDependencyGraph(relationsToAdd, true);
[2512]231 newToAdd.addAll(graph.computeUploadOrder());
232 toAdd = newToAdd;
[6776]233
[8338]234 List<OsmPrimitive> newToDelete = new LinkedList<>();
[6776]235 graph = new RelationUploadDependencyGraph(Utils.filteredCollection(toDelete, Relation.class), false);
236 newToDelete.addAll(graph.computeUploadOrder());
[6801]237 newToDelete.addAll(Utils.filteredCollection(toDelete, Way.class));
238 newToDelete.addAll(Utils.filteredCollection(toDelete, Node.class));
[6776]239 toDelete = newToDelete;
[2512]240 }
241
242 /**
243 * Replies the subset of relations in <code>relations</code> which are not referring to any
244 * new relation
245 *
246 * @param relations a list of relations
247 * @return the subset of relations in <code>relations</code> which are not referring to any
248 * new relation
249 */
250 protected List<Relation> filterRelationsNotReferringToNewRelations(Collection<Relation> relations) {
[7005]251 List<Relation> ret = new LinkedList<>();
[2512]252 for (Relation relation: relations) {
253 boolean refersToNewRelation = false;
254 for (RelationMember m : relation.getMembers()) {
[3336]255 if (m.isRelation() && m.getMember().isNewOrUndeleted()) {
[2512]256 refersToNewRelation = true;
257 break;
258 }
259 }
260 if (!refersToNewRelation) {
261 ret.add(relation);
262 }
263 }
264 return ret;
265 }
266
267 /**
[2915]268 * Utility class to sort a collection of new relations with their dependencies
[2512]269 * topologically.
270 *
271 */
[4874]272 private static class RelationUploadDependencyGraph {
[7005]273 private Map<Relation, Set<Relation>> children = new HashMap<>();
[2512]274 private Collection<Relation> relations;
[7005]275 private Set<Relation> visited = new HashSet<>();
[2512]276 private List<Relation> uploadOrder;
[6776]277 private final boolean newOrUndeleted;
[2512]278
[8836]279 RelationUploadDependencyGraph(Collection<Relation> relations, boolean newOrUndeleted) {
[6776]280 this.newOrUndeleted = newOrUndeleted;
[2512]281 build(relations);
282 }
283
[6890]284 public final void build(Collection<Relation> relations) {
[7005]285 this.relations = new HashSet<>();
[8510]286 for (Relation relation: relations) {
[6776]287 if (newOrUndeleted ? !relation.isNewOrUndeleted() : !relation.isDeleted()) {
[2512]288 continue;
289 }
290 this.relations.add(relation);
291 for (RelationMember m: relation.getMembers()) {
[6776]292 if (m.isRelation() && (newOrUndeleted ? m.getMember().isNewOrUndeleted() : m.getMember().isDeleted())) {
[8510]293 addDependency(relation, (Relation) m.getMember());
[2512]294 }
295 }
296 }
297 }
298
299 public Set<Relation> getChildren(Relation relation) {
300 Set<Relation> p = children.get(relation);
301 if (p == null) {
[7005]302 p = new HashSet<>();
[2512]303 children.put(relation, p);
304 }
305 return p;
306 }
307
308 public void addDependency(Relation relation, Relation child) {
309 getChildren(relation).add(child);
310 }
311
[8851]312 protected void visit(Deque<Relation> path, Relation current) throws CyclicUploadDependencyException {
[2512]313 if (path.contains(current)) {
314 path.push(current);
315 throw new CyclicUploadDependencyException(path);
316 }
317 if (!visited.contains(current)) {
318 path.push(current);
319 visited.add(current);
320 for (Relation dependent : getChildren(current)) {
[8510]321 visit(path, dependent);
[2512]322 }
323 uploadOrder.add(current);
324 path.pop();
325 }
326 }
327
328 public List<Relation> computeUploadOrder() throws CyclicUploadDependencyException {
[7005]329 visited = new HashSet<>();
330 uploadOrder = new LinkedList<>();
[8851]331 Deque<Relation> path = new ArrayDeque<>();
[2512]332 for (Relation relation: relations) {
333 visit(path, relation);
334 }
[7005]335 List<Relation> ret = new ArrayList<>(relations);
[2512]336 Collections.sort(
337 ret,
338 new Comparator<Relation>() {
[6084]339 @Override
[2512]340 public int compare(Relation o1, Relation o2) {
[8365]341 return Integer.compare(uploadOrder.indexOf(o1), uploadOrder.indexOf(o2));
[2512]342 }
343 }
[4874]344 );
[2512]345 return ret;
346 }
347 }
348}
Note: See TracBrowser for help on using the repository browser.