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 }