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

Last change on this file since 2915 was 2915, checked in by stoecker, 14 years ago

close #4458 - text fixes - patch by Andre

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