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

Last change on this file since 2198 was 2188, checked in by Gubaer, 15 years ago

fixed #3570: uploated deletions create conflicts at next upload

File size: 9.0 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.data.osm.DataSet;
17import org.openstreetmap.josm.data.osm.Node;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.data.osm.Relation;
20import org.openstreetmap.josm.data.osm.RelationMember;
21import org.openstreetmap.josm.data.osm.Way;
22
23import org.openstreetmap.josm.actions.upload.CyclicUploadDependencyException;
24
25/**
26 * Represents a collection of {@see OsmPrimitive}s which should be uploaded to the
27 * API.
28 * The collection is derived from the modified primitives of an {@see DataSet}.
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.getId() == 0 && !osm.isDeleted()) {
61 toAdd.addLast(osm);
62 } else if (osm.isModified() && !osm.isDeleted()) {
63 toUpdate.addLast(osm);
64 } else if (osm.isDeleted() && osm.getId() != 0 && osm.isModified()) {
65 toDelete.addFirst(osm);
66 }
67 }
68 }
69
70 /**
71 * initializes the API data set with the modified primitives in <code>ds</code>
72 *
73 * @param ds the data set. Ignored, if null.
74 */
75 public APIDataSet(DataSet ds) {
76 this();
77 init(ds);
78 }
79
80 /**
81 * Replies true if there are no primitives to upload
82 *
83 * @return true if there are no primitives to upload
84 */
85 public boolean isEmpty() {
86 return toAdd.isEmpty() && toUpdate.isEmpty() && toDelete.isEmpty();
87 }
88
89 /**
90 * Replies the primitives which should be added to the OSM database
91 *
92 * @return the primitives which should be added to the OSM database
93 */
94 public List<OsmPrimitive> getPrimitivesToAdd() {
95 return toAdd;
96 }
97
98 /**
99 * Replies the primitives which should be updated in the OSM database
100 *
101 * @return the primitives which should be updated in the OSM database
102 */
103 public List<OsmPrimitive> getPrimitivesToUpdate() {
104 return toUpdate;
105 }
106
107 /**
108 * Replies the primitives which should be deleted in the OSM database
109 *
110 * @return the primitives which should be deleted in the OSM database
111 */
112 public List<OsmPrimitive> getPrimitivesToDelete() {
113 return toDelete;
114 }
115
116 /**
117 * Replies all primitives
118 *
119 * @return all primitives
120 */
121 public List<OsmPrimitive> getPrimitives() {
122 LinkedList<OsmPrimitive> ret = new LinkedList<OsmPrimitive>();
123 ret.addAll(toAdd);
124 ret.addAll(toUpdate);
125 ret.addAll(toDelete);
126 return ret;
127 }
128
129 /**
130 * Adjusts the upload order for new relations. Child relations are uploaded first,
131 * parent relations second.
132 *
133 * This method detects cyclic dependencies in new relation. Relations with cyclic
134 * dependencies can't be uploaded.
135 *
136 * @throws CyclicUploadDependencyException thrown, if a cyclic dependency is detected
137 */
138 public void adjustRelationUploadOrder() throws CyclicUploadDependencyException{
139 LinkedList<OsmPrimitive> newToAdd = new LinkedList<OsmPrimitive>();
140 newToAdd.addAll(OsmPrimitive.getFilteredList(toAdd, Node.class));
141 newToAdd.addAll(OsmPrimitive.getFilteredList(toAdd, Way.class));
142
143 List<Relation> relationsToAdd = OsmPrimitive.getFilteredList(toAdd, Relation.class);
144 List<Relation> noProblemRelations = filterRelationsNotReferringToNewRelations(relationsToAdd);
145 newToAdd.addAll(noProblemRelations);
146 relationsToAdd.removeAll(noProblemRelations);
147
148 RelationUploadDependencyGraph graph = new RelationUploadDependencyGraph(relationsToAdd);
149 newToAdd.addAll(graph.computeUploadOrder());
150 toAdd = newToAdd;
151 }
152
153 /**
154 * Replies the subset of relations in <code>relations</code> which are not referring to any
155 * new relation
156 *
157 * @param relations a list of relations
158 * @return the subset of relations in <code>relations</code> which are not referring to any
159 * new relation
160 */
161 protected List<Relation> filterRelationsNotReferringToNewRelations(Collection<Relation> relations) {
162 List<Relation> ret = new LinkedList<Relation>();
163 for (Relation relation: relations) {
164 boolean refersToNewRelation = false;
165 for (RelationMember m : relation.getMembers()) {
166 if (m.isRelation() && m.getMember().getId() <= 0) {
167 refersToNewRelation = true;
168 break;
169 }
170 }
171 if (!refersToNewRelation) {
172 ret.add(relation);
173 }
174 }
175 return ret;
176 }
177
178 /**
179 * Utility class to sort a collection of of new relations with their dependencies
180 * topologically.
181 *
182 */
183 private class RelationUploadDependencyGraph {
184 private final Logger logger = Logger.getLogger(RelationUploadDependencyGraph.class.getName());
185 private HashMap<Relation, Set<Relation>> children;
186 private Collection<Relation> relations;
187 private Set<Relation> visited;
188 private List<Relation> uploadOrder;
189
190 public RelationUploadDependencyGraph() {
191 this.children = new HashMap<Relation, Set<Relation>>();
192 this.visited = new HashSet<Relation>();
193 }
194
195 public RelationUploadDependencyGraph(Collection<Relation> relations) {
196 this();
197 build(relations);
198 }
199
200 public void build(Collection<Relation> relations) {
201 this.relations = new HashSet<Relation>();
202 for(Relation relation: relations) {
203 if (relation.getId() > 0 ) {
204 continue;
205 }
206 this.relations.add(relation);
207 for (RelationMember m: relation.getMembers()) {
208 if (m.isRelation() && m.getMember().getId() == 0) {
209 addDependency(relation, (Relation)m.getMember());
210 }
211 }
212 }
213 }
214
215 public Set<Relation> getChildren(Relation relation) {
216 Set<Relation> p = children.get(relation);
217 if (p == null) {
218 p = new HashSet<Relation>();
219 children.put(relation, p);
220 }
221 return p;
222 }
223
224 public void addDependency(Relation relation, Relation child) {
225 getChildren(relation).add(child);
226 }
227
228 protected void visit(Stack<Relation> path, Relation current) throws CyclicUploadDependencyException{
229 if (path.contains(current)) {
230 path.push(current);
231 throw new CyclicUploadDependencyException(path);
232 }
233 if (!visited.contains(current)) {
234 path.push(current);
235 visited.add(current);
236 for (Relation dependent : getChildren(current)) {
237 visit(path,dependent);
238 }
239 uploadOrder.add(current);
240 path.pop();
241 }
242 }
243
244 public List<Relation> computeUploadOrder() throws CyclicUploadDependencyException {
245 visited = new HashSet<Relation>();
246 uploadOrder = new LinkedList<Relation>();
247 Stack<Relation> path = new Stack<Relation>();
248 for (Relation relation: relations) {
249 visit(path, relation);
250 }
251 ArrayList<Relation> ret = new ArrayList<Relation>(relations);
252 Collections.sort(
253 ret,
254 new Comparator<Relation>() {
255 public int compare(Relation o1, Relation o2) {
256 return new Integer(uploadOrder.indexOf(o1)).compareTo(uploadOrder.indexOf(o2));
257 }
258 }
259 );
260 return ret;
261 }
262 }
263}
Note: See TracBrowser for help on using the repository browser.