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 }