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