1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.data.osm;
|
---|
3 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
4 |
|
---|
5 | import java.util.Collections;
|
---|
6 | import java.util.HashMap;
|
---|
7 | import java.util.Iterator;
|
---|
8 | import java.util.Map;
|
---|
9 | import java.util.Map.Entry;
|
---|
10 | import java.util.Set;
|
---|
11 |
|
---|
12 | import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
|
---|
13 | import org.openstreetmap.josm.tools.CheckParameterUtil;
|
---|
14 | import org.openstreetmap.josm.tools.Logging;
|
---|
15 |
|
---|
16 | /**
|
---|
17 | * A ChangesetDataSet holds the content of a changeset. Typically, a primitive is modified only once in a changeset,
|
---|
18 | * but if there are multiple modifications, the first and last are kept. Further intermediate versions are not kept.
|
---|
19 | */
|
---|
20 | public class ChangesetDataSet {
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * Type of primitive modification.
|
---|
24 | */
|
---|
25 | public enum ChangesetModificationType {
|
---|
26 | /** The primitive has been created */
|
---|
27 | CREATED,
|
---|
28 | /** The primitive has been updated */
|
---|
29 | UPDATED,
|
---|
30 | /** The primitive has been deleted */
|
---|
31 | DELETED
|
---|
32 | }
|
---|
33 |
|
---|
34 | /**
|
---|
35 | * An entry in the changeset dataset.
|
---|
36 | */
|
---|
37 | public interface ChangesetDataSetEntry {
|
---|
38 |
|
---|
39 | /**
|
---|
40 | * Returns the type of modification.
|
---|
41 | * @return the type of modification
|
---|
42 | */
|
---|
43 | ChangesetModificationType getModificationType();
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * Returns the affected history primitive.
|
---|
47 | * @return the affected history primitive
|
---|
48 | */
|
---|
49 | HistoryOsmPrimitive getPrimitive();
|
---|
50 | }
|
---|
51 |
|
---|
52 | /** maps an id to either one {@link ChangesetDataSetEntry} or an array of {@link ChangesetDataSetEntry} */
|
---|
53 | private final Map<PrimitiveId, Object> entryMap = new HashMap<>();
|
---|
54 |
|
---|
55 | /**
|
---|
56 | * Remembers a history primitive with the given modification type
|
---|
57 | *
|
---|
58 | * @param primitive the primitive. Must not be null.
|
---|
59 | * @param cmt the modification type. Must not be null.
|
---|
60 | * @throws IllegalArgumentException if primitive is null
|
---|
61 | * @throws IllegalArgumentException if cmt is null
|
---|
62 | * @throws IllegalArgumentException if the same primitive was already stored with a higher or equal version
|
---|
63 | */
|
---|
64 | public void put(HistoryOsmPrimitive primitive, ChangesetModificationType cmt) {
|
---|
65 | CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
|
---|
66 | CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
|
---|
67 | DefaultChangesetDataSetEntry csEntry = new DefaultChangesetDataSetEntry(cmt, primitive);
|
---|
68 | Object val = entryMap.get(primitive.getPrimitiveId());
|
---|
69 | ChangesetDataSetEntry[] entries;
|
---|
70 | if (val == null) {
|
---|
71 | entryMap.put(primitive.getPrimitiveId(), csEntry);
|
---|
72 | return;
|
---|
73 | }
|
---|
74 | if (val instanceof ChangesetDataSetEntry) {
|
---|
75 | entries = new ChangesetDataSetEntry[2];
|
---|
76 | entries[0] = (ChangesetDataSetEntry) val;
|
---|
77 | if (primitive.getVersion() <= entries[0].getPrimitive().getVersion()) {
|
---|
78 | throw new IllegalArgumentException(
|
---|
79 | tr("Changeset {0}: Unexpected order of versions for {1}: v{2} is not higher than v{3}",
|
---|
80 | String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
|
---|
81 | primitive.getVersion(), entries[0].getPrimitive().getVersion()));
|
---|
82 | }
|
---|
83 | } else {
|
---|
84 | entries = (ChangesetDataSetEntry[]) val;
|
---|
85 | }
|
---|
86 | if (entries[1] != null) {
|
---|
87 | Logging.info("Changeset {0}: Change of {1} v{2} is replaced by version v{3}",
|
---|
88 | String.valueOf(primitive.getChangesetId()), primitive.getPrimitiveId(),
|
---|
89 | entries[1].getPrimitive().getVersion(), primitive.getVersion());
|
---|
90 | }
|
---|
91 | entries[1] = csEntry;
|
---|
92 | entryMap.put(primitive.getPrimitiveId(), entries);
|
---|
93 | }
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * Replies true if the changeset content contains the object with primitive <code>id</code>.
|
---|
97 | * @param id the id.
|
---|
98 | * @return true if the changeset content contains the object with primitive <code>id</code>
|
---|
99 | */
|
---|
100 | public boolean contains(PrimitiveId id) {
|
---|
101 | if (id == null) return false;
|
---|
102 | return entryMap.containsKey(id);
|
---|
103 | }
|
---|
104 |
|
---|
105 | /**
|
---|
106 | * Replies the last modification type for the object with id <code>id</code>. Replies null, if id is null or
|
---|
107 | * if the object with id <code>id</code> isn't in the changeset content.
|
---|
108 | *
|
---|
109 | * @param id the id
|
---|
110 | * @return the last modification type or null
|
---|
111 | */
|
---|
112 | public ChangesetModificationType getModificationType(PrimitiveId id) {
|
---|
113 | ChangesetDataSetEntry e = getLastEntry(id);
|
---|
114 | return e != null ? e.getModificationType() : null;
|
---|
115 | }
|
---|
116 |
|
---|
117 | /**
|
---|
118 | * Replies true if the primitive with id <code>id</code> was created in this
|
---|
119 | * changeset. Replies false, if id is null or not in the dataset.
|
---|
120 | *
|
---|
121 | * @param id the id
|
---|
122 | * @return true if the primitive with id <code>id</code> was created in this
|
---|
123 | * changeset.
|
---|
124 | */
|
---|
125 | public boolean isCreated(PrimitiveId id) {
|
---|
126 | ChangesetDataSetEntry e = getFirstEntry(id);
|
---|
127 | return e != null && e.getModificationType() == ChangesetModificationType.CREATED;
|
---|
128 | }
|
---|
129 |
|
---|
130 | /**
|
---|
131 | * Replies true if the primitive with id <code>id</code> was updated in this
|
---|
132 | * changeset. Replies false, if id is null or not in the dataset.
|
---|
133 | *
|
---|
134 | * @param id the id
|
---|
135 | * @return true if the primitive with id <code>id</code> was updated in this
|
---|
136 | * changeset.
|
---|
137 | */
|
---|
138 | public boolean isUpdated(PrimitiveId id) {
|
---|
139 | ChangesetDataSetEntry e = getLastEntry(id);
|
---|
140 | return e != null && e.getModificationType() == ChangesetModificationType.UPDATED;
|
---|
141 | }
|
---|
142 |
|
---|
143 | /**
|
---|
144 | * Replies true if the primitive with id <code>id</code> was deleted in this
|
---|
145 | * changeset. Replies false, if id is null or not in the dataset.
|
---|
146 | *
|
---|
147 | * @param id the id
|
---|
148 | * @return true if the primitive with id <code>id</code> was deleted in this
|
---|
149 | * changeset.
|
---|
150 | */
|
---|
151 | public boolean isDeleted(PrimitiveId id) {
|
---|
152 | ChangesetDataSetEntry e = getLastEntry(id);
|
---|
153 | return e != null && e.getModificationType() == ChangesetModificationType.DELETED;
|
---|
154 | }
|
---|
155 |
|
---|
156 | /**
|
---|
157 | * Replies the number of primitives in the dataset.
|
---|
158 | *
|
---|
159 | * @return the number of primitives in the dataset.
|
---|
160 | */
|
---|
161 | public int size() {
|
---|
162 | return entryMap.size();
|
---|
163 | }
|
---|
164 |
|
---|
165 | /**
|
---|
166 | * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
|
---|
167 | * null, if there is no such primitive in the data set. If the primitive was modified
|
---|
168 | * multiple times, the last version is returned.
|
---|
169 | *
|
---|
170 | * @param id the id
|
---|
171 | * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
|
---|
172 | */
|
---|
173 | public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
|
---|
174 | ChangesetDataSetEntry e = getLastEntry(id);
|
---|
175 | return e != null ? e.getPrimitive() : null;
|
---|
176 | }
|
---|
177 |
|
---|
178 | /**
|
---|
179 | * Returns an unmodifiable set of all primitives in this dataset.
|
---|
180 | * @return an unmodifiable set of all primitives in this dataset.
|
---|
181 | * @since 14946
|
---|
182 | */
|
---|
183 | public Set<PrimitiveId> getIds() {
|
---|
184 | return Collections.unmodifiableSet(entryMap.keySet());
|
---|
185 | }
|
---|
186 |
|
---|
187 | /**
|
---|
188 | * Replies the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
|
---|
189 | * null, if there is no such primitive in the data set.
|
---|
190 | * @param id the id
|
---|
191 | * @return the first {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
|
---|
192 | * @since 14946
|
---|
193 | */
|
---|
194 | public ChangesetDataSetEntry getFirstEntry(PrimitiveId id) {
|
---|
195 | return getEntry(id, 0);
|
---|
196 | }
|
---|
197 |
|
---|
198 | /**
|
---|
199 | * Replies the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset.
|
---|
200 | * null, if there is no such primitive in the data set.
|
---|
201 | * @param id the id
|
---|
202 | * @return the last {@link ChangesetDataSetEntry} with id <code>id</code> from this dataset or null.
|
---|
203 | * @since 14946
|
---|
204 | */
|
---|
205 | public ChangesetDataSetEntry getLastEntry(PrimitiveId id) {
|
---|
206 | return getEntry(id, 1);
|
---|
207 | }
|
---|
208 |
|
---|
209 | private ChangesetDataSetEntry getEntry(PrimitiveId id, int n) {
|
---|
210 | if (id == null)
|
---|
211 | return null;
|
---|
212 | Object val = entryMap.get(id);
|
---|
213 | if (val == null)
|
---|
214 | return null;
|
---|
215 | if (val instanceof ChangesetDataSetEntry[]) {
|
---|
216 | ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
|
---|
217 | return entries[n];
|
---|
218 | } else {
|
---|
219 | return (ChangesetDataSetEntry) val;
|
---|
220 | }
|
---|
221 | }
|
---|
222 |
|
---|
223 | /**
|
---|
224 | * Returns an iterator over dataset entries. The elements are returned in no particular order.
|
---|
225 | * @return an iterator over dataset entries. If a primitive was changed multiple times, only the last entry is returned.
|
---|
226 | */
|
---|
227 | public Iterator<ChangesetDataSetEntry> iterator() {
|
---|
228 | return new DefaultIterator(entryMap);
|
---|
229 | }
|
---|
230 |
|
---|
231 | /**
|
---|
232 | * Class to keep one entry of a changeset: the combination of modification type and primitive.
|
---|
233 | */
|
---|
234 | public static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
|
---|
235 | private final ChangesetModificationType modificationType;
|
---|
236 | private final HistoryOsmPrimitive primitive;
|
---|
237 |
|
---|
238 | /**
|
---|
239 | * Construct new entry.
|
---|
240 | * @param modificationType the modification type
|
---|
241 | * @param primitive the primitive
|
---|
242 | */
|
---|
243 | public DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
|
---|
244 | this.modificationType = modificationType;
|
---|
245 | this.primitive = primitive;
|
---|
246 | }
|
---|
247 |
|
---|
248 | @Override
|
---|
249 | public ChangesetModificationType getModificationType() {
|
---|
250 | return modificationType;
|
---|
251 | }
|
---|
252 |
|
---|
253 | @Override
|
---|
254 | public HistoryOsmPrimitive getPrimitive() {
|
---|
255 | return primitive;
|
---|
256 | }
|
---|
257 |
|
---|
258 | @Override
|
---|
259 | public String toString() {
|
---|
260 | return modificationType.toString() + " " + primitive.toString();
|
---|
261 | }
|
---|
262 | }
|
---|
263 |
|
---|
264 | private static class DefaultIterator implements Iterator<ChangesetDataSetEntry> {
|
---|
265 | private final Iterator<Entry<PrimitiveId, Object>> typeIterator;
|
---|
266 |
|
---|
267 | DefaultIterator(Map<PrimitiveId, Object> entryMap) {
|
---|
268 | typeIterator = entryMap.entrySet().iterator();
|
---|
269 | }
|
---|
270 |
|
---|
271 | @Override
|
---|
272 | public boolean hasNext() {
|
---|
273 | return typeIterator.hasNext();
|
---|
274 | }
|
---|
275 |
|
---|
276 | @Override
|
---|
277 | public ChangesetDataSetEntry next() {
|
---|
278 | Entry<PrimitiveId, Object> next = typeIterator.next();
|
---|
279 | // get last entry
|
---|
280 | Object val = next.getValue();
|
---|
281 | ChangesetDataSetEntry last;
|
---|
282 | if (val instanceof ChangesetDataSetEntry[]) {
|
---|
283 | ChangesetDataSetEntry[] entries = (ChangesetDataSetEntry[]) val;
|
---|
284 | last = entries[1];
|
---|
285 | } else {
|
---|
286 | last = (ChangesetDataSetEntry) val;
|
---|
287 | }
|
---|
288 | return new DefaultChangesetDataSetEntry(last.getModificationType(), last.getPrimitive());
|
---|
289 | }
|
---|
290 |
|
---|
291 | @Override
|
---|
292 | public void remove() {
|
---|
293 | throw new UnsupportedOperationException();
|
---|
294 | }
|
---|
295 | }
|
---|
296 | }
|
---|