001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.osm;
003
004import java.util.HashMap;
005import java.util.Iterator;
006import java.util.Map;
007import java.util.Map.Entry;
008import java.util.Set;
009import java.util.stream.Collectors;
010
011import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
012import org.openstreetmap.josm.tools.CheckParameterUtil;
013
014/**
015 * A ChangesetDataSet holds the content of a changeset.
016 */
017public class ChangesetDataSet {
018
019    /**
020     * Type of primitive modification.
021     */
022    public enum ChangesetModificationType {
023        /** The primitive has been created */
024        CREATED,
025        /** The primitive has been updated */
026        UPDATED,
027        /** The primitive has been deleted */
028        DELETED
029    }
030
031    /**
032     * An entry in the changeset dataset.
033     */
034    public interface ChangesetDataSetEntry {
035
036        /**
037         * Returns the type of modification.
038         * @return the type of modification
039         */
040        ChangesetModificationType getModificationType();
041
042        /**
043         * Returns the affected history primitive.
044         * @return the affected history primitive
045         */
046        HistoryOsmPrimitive getPrimitive();
047    }
048
049    private final Map<PrimitiveId, HistoryOsmPrimitive> primitives = new HashMap<>();
050    private final Map<PrimitiveId, ChangesetModificationType> modificationTypes = new HashMap<>();
051
052    /**
053     * Remembers a history primitive with the given modification type
054     *
055     * @param primitive the primitive. Must not be null.
056     * @param cmt the modification type. Must not be null.
057     * @throws IllegalArgumentException if primitive is null
058     * @throws IllegalArgumentException if cmt is null
059     */
060    public void put(HistoryOsmPrimitive primitive, ChangesetModificationType cmt) {
061        CheckParameterUtil.ensureParameterNotNull(primitive, "primitive");
062        CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
063        primitives.put(primitive.getPrimitiveId(), primitive);
064        modificationTypes.put(primitive.getPrimitiveId(), cmt);
065    }
066
067    /**
068     * Replies true if the changeset content contains the object with primitive <code>id</code>.
069     * @param id the id.
070     * @return true if the changeset content contains the object with primitive <code>id</code>
071     */
072    public boolean contains(PrimitiveId id) {
073        if (id == null) return false;
074        return primitives.containsKey(id);
075    }
076
077    /**
078     * Replies the modification type for the object with id <code>id</code>. Replies null, if id is null or
079     * if the object with id <code>id</code> isn't in the changeset content.
080     *
081     * @param id the id
082     * @return the modification type
083     */
084    public ChangesetModificationType getModificationType(PrimitiveId id) {
085        if (!contains(id)) return null;
086        return modificationTypes.get(id);
087    }
088
089    /**
090     * Replies true if the primitive with id <code>id</code> was created in this
091     * changeset. Replies false, if id is null.
092     *
093     * @param id the id
094     * @return true if the primitive with id <code>id</code> was created in this
095     * changeset.
096     */
097    public boolean isCreated(PrimitiveId id) {
098        if (!contains(id)) return false;
099        return ChangesetModificationType.CREATED == getModificationType(id);
100    }
101
102    /**
103     * Replies true if the primitive with id <code>id</code> was updated in this
104     * changeset. Replies false, if id is null.
105     *
106     * @param id the id
107     * @return true if the primitive with id <code>id</code> was updated in this
108     * changeset.
109     */
110    public boolean isUpdated(PrimitiveId id) {
111        if (!contains(id)) return false;
112        return ChangesetModificationType.UPDATED == getModificationType(id);
113    }
114
115    /**
116     * Replies true if the primitive with id <code>id</code> was deleted in this
117     * changeset. Replies false, if id is null.
118     *
119     * @param id the id
120     * @return true if the primitive with id <code>id</code> was deleted in this
121     * changeset.
122     */
123    public boolean isDeleted(PrimitiveId id) {
124        if (!contains(id)) return false;
125        return ChangesetModificationType.DELETED == getModificationType(id);
126    }
127
128    /**
129     * Replies the set of primitives with a specific modification type
130     *
131     * @param cmt the modification type. Must not be null.
132     * @return the set of primitives
133     * @throws IllegalArgumentException if cmt is null
134     */
135    public Set<HistoryOsmPrimitive> getPrimitivesByModificationType(ChangesetModificationType cmt) {
136        CheckParameterUtil.ensureParameterNotNull(cmt, "cmt");
137        return modificationTypes.entrySet().stream()
138                .filter(entry -> entry.getValue() == cmt)
139                .map(entry -> primitives.get(entry.getKey()))
140                .collect(Collectors.toSet());
141    }
142
143    /**
144     * Replies the number of objects in the dataset
145     *
146     * @return the number of objects in the dataset
147     */
148    public int size() {
149        return primitives.size();
150    }
151
152    /**
153     * Replies the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset.
154     * null, if there is no such primitive in the data set.
155     *
156     * @param id the id
157     * @return the {@link HistoryOsmPrimitive} with id <code>id</code> from this dataset
158     */
159    public HistoryOsmPrimitive getPrimitive(PrimitiveId id) {
160        if (id == null) return null;
161        return primitives.get(id);
162    }
163
164    /**
165     * Returns an iterator over dataset entries.
166     * @return an iterator over dataset entries
167     */
168    public Iterator<ChangesetDataSetEntry> iterator() {
169        return new DefaultIterator();
170    }
171
172    private static class DefaultChangesetDataSetEntry implements ChangesetDataSetEntry {
173        private final ChangesetModificationType modificationType;
174        private final HistoryOsmPrimitive primitive;
175
176        DefaultChangesetDataSetEntry(ChangesetModificationType modificationType, HistoryOsmPrimitive primitive) {
177            this.modificationType = modificationType;
178            this.primitive = primitive;
179        }
180
181        @Override
182        public ChangesetModificationType getModificationType() {
183            return modificationType;
184        }
185
186        @Override
187        public HistoryOsmPrimitive getPrimitive() {
188            return primitive;
189        }
190    }
191
192    private class DefaultIterator implements Iterator<ChangesetDataSetEntry> {
193        private final Iterator<Entry<PrimitiveId, ChangesetModificationType>> typeIterator;
194
195        DefaultIterator() {
196            typeIterator = modificationTypes.entrySet().iterator();
197        }
198
199        @Override
200        public boolean hasNext() {
201            return typeIterator.hasNext();
202        }
203
204        @Override
205        public ChangesetDataSetEntry next() {
206            Entry<PrimitiveId, ChangesetModificationType> next = typeIterator.next();
207            return new DefaultChangesetDataSetEntry(next.getValue(), primitives.get(next.getKey()));
208        }
209
210        @Override
211        public void remove() {
212            throw new UnsupportedOperationException();
213        }
214    }
215}