001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.data.osm.history;
003    
004    import java.text.MessageFormat;
005    import java.util.ArrayList;
006    import java.util.HashMap;
007    import java.util.concurrent.CopyOnWriteArrayList;
008    
009    import org.openstreetmap.josm.Main;
010    import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
011    import org.openstreetmap.josm.data.osm.PrimitiveId;
012    import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
013    import org.openstreetmap.josm.gui.MapView;
014    import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
015    import org.openstreetmap.josm.gui.layer.Layer;
016    import org.openstreetmap.josm.tools.CheckParameterUtil;
017    
018    /**
019     * A data set holding histories of OSM primitives.
020     *
021     *
022     */
023    public class HistoryDataSet implements LayerChangeListener{
024        /** the unique instance */
025        private static HistoryDataSet historyDataSet;
026    
027        /**
028         * Replies the unique instance of the history data set
029         *
030         * @return the unique instance of the history data set
031         */
032        public static HistoryDataSet getInstance() {
033            if (historyDataSet == null) {
034                historyDataSet = new HistoryDataSet();
035                MapView.addLayerChangeListener(historyDataSet);
036            }
037            return  historyDataSet;
038        }
039    
040        /** the history data */
041        private HashMap<PrimitiveId, ArrayList<HistoryOsmPrimitive>> data;
042        private CopyOnWriteArrayList<HistoryDataSetListener> listeners;
043    
044        public HistoryDataSet() {
045            data = new HashMap<PrimitiveId, ArrayList<HistoryOsmPrimitive>>();
046            listeners = new CopyOnWriteArrayList<HistoryDataSetListener>();
047        }
048    
049        public void addHistoryDataSetListener(HistoryDataSetListener listener) {
050            if (listener != null) {
051                listeners.addIfAbsent(listener);
052            }
053        }
054    
055        public void removeHistoryDataSetListener(HistoryDataSetListener listener) {
056            listeners.remove(listener);
057        }
058    
059        protected void fireHistoryUpdated(PrimitiveId id) {
060            for (HistoryDataSetListener l : listeners) {
061                l.historyUpdated(this, id);
062            }
063        }
064    
065        protected void fireCacheCleared() {
066            for (HistoryDataSetListener l : listeners) {
067                l.historyDataSetCleared(this);
068            }
069        }
070    
071        /**
072         * Replies the history primitive for the primitive with id <code>id</code>
073         * and version <code>version</code>. null, if no such primitive exists.
074         *
075         * @param id the id of the primitive. > 0 required.
076         * @param type the primitive type. Must not be null.
077         * @param version the version of the primitive. > 0 required
078         * @return the history primitive for the primitive with id <code>id</code>,
079         * type <code>type</code>, and version <code>version</code>
080         */
081        public HistoryOsmPrimitive get(long id, OsmPrimitiveType type, long version){
082            if (id <= 0)
083                throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected, got {1}", "id", id));
084            CheckParameterUtil.ensureParameterNotNull(type, "type");
085            if (version <= 0)
086                throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected, got {1}", "version", version));
087    
088            SimplePrimitiveId pid = new SimplePrimitiveId(id, type);
089            ArrayList<HistoryOsmPrimitive> versions = data.get(pid);
090            if (versions == null)
091                return null;
092            for (HistoryOsmPrimitive primitive: versions) {
093                if (primitive.matches(id, version))
094                    return primitive;
095            }
096            return null;
097        }
098    
099        /**
100         * Adds a history primitive to the data set
101         *
102         * @param primitive  the history primitive to add
103         */
104        public void put(HistoryOsmPrimitive primitive) {
105            PrimitiveId id = new SimplePrimitiveId(primitive.getId(), primitive.getType());
106            if (data.get(id) == null) {
107                data.put(id, new ArrayList<HistoryOsmPrimitive>());
108            }
109            data.get(id).add(primitive);
110            fireHistoryUpdated(id);
111        }
112    
113        /**
114         * Replies the history for a given primitive with id <code>id</code>
115         * and type <code>type</code>.
116         *
117         * @param id the id the if of the primitive. > 0 required
118         * @param type the type of the primitive. Must not be null.
119         * @return the history. null, if there isn't a history for <code>id</code> and
120         * <code>type</code>.
121         * @throws IllegalArgumentException thrown if id <= 0
122         * @throws IllegalArgumentException thrown if type is null
123         */
124        public History getHistory(long id, OsmPrimitiveType type) throws IllegalArgumentException{
125            if (id <= 0)
126                throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected, got {1}", "id", id));
127            CheckParameterUtil.ensureParameterNotNull(type, "type");
128            SimplePrimitiveId pid = new SimplePrimitiveId(id, type);
129            return getHistory(pid);
130        }
131    
132        /**
133         * Replies the history for a primitive with id <code>id</code>. null, if no
134         * such history exists.
135         *
136         * @param pid the primitive id. Must not be null.
137         * @return the history for a primitive with id <code>id</code>. null, if no
138         * such history exists
139         * @throws IllegalArgumentException thrown if pid is null
140         */
141        public History getHistory(PrimitiveId pid) throws IllegalArgumentException{
142            CheckParameterUtil.ensureParameterNotNull(pid, "pid");
143            ArrayList<HistoryOsmPrimitive> versions = data.get(pid);
144            if (versions == null)
145                return null;
146            return new History(pid.getUniqueId(), pid.getType(), versions);
147        }
148    
149        /**
150         * merges the histories from the {@link HistoryDataSet} other in this history data set
151         *
152         * @param other the other history data set. Ignored if null.
153         */
154        public void mergeInto(HistoryDataSet other) {
155            if (other == null)
156                return;
157            for (PrimitiveId id : other.data.keySet()) {
158                this.data.put(id, other.data.get(id));
159            }
160            fireHistoryUpdated(null);
161        }
162    
163        /* ------------------------------------------------------------------------------ */
164        /* interface LayerChangeListener                                                  */
165        /* ------------------------------------------------------------------------------ */
166        public void activeLayerChange(Layer oldLayer, Layer newLayer) {/* irrelevant in this context */}
167        public void layerAdded(Layer newLayer) {/* irrelevant in this context */}
168        public void layerRemoved(Layer oldLayer) {
169            if (Main.map == null || Main.map.mapView == null) return;
170            if (Main.map.mapView.getNumLayers() == 0) {
171                data.clear();
172                fireCacheCleared();
173            }
174        }
175    }