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.Collections; 007 import java.util.Comparator; 008 import java.util.Date; 009 import java.util.List; 010 011 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 012 import org.openstreetmap.josm.data.osm.PrimitiveId; 013 import org.openstreetmap.josm.data.osm.SimplePrimitiveId; 014 import org.openstreetmap.josm.tools.CheckParameterUtil; 015 016 /** 017 * Represents the history of an OSM primitive. The history consists 018 * of a list of object snapshots with a specific version. 019 * 020 */ 021 public class History{ 022 private static interface FilterPredicate { 023 boolean matches(HistoryOsmPrimitive primitive); 024 } 025 026 private static History filter(History history, FilterPredicate predicate) { 027 ArrayList<HistoryOsmPrimitive> out = new ArrayList<HistoryOsmPrimitive>(); 028 for (HistoryOsmPrimitive primitive: history.versions) { 029 if (predicate.matches(primitive)) { 030 out.add(primitive); 031 } 032 } 033 return new History(history.id, history.type,out); 034 } 035 036 /** the list of object snapshots */ 037 private ArrayList<HistoryOsmPrimitive> versions; 038 /** the object id */ 039 private final long id; 040 private final OsmPrimitiveType type; 041 042 /** 043 * Creates a new history for an OSM primitive 044 * 045 * @param id the id. >0 required. 046 * @param type the primitive type. Must not be null. 047 * @param versions a list of versions. Can be null. 048 * @throws IllegalArgumentException thrown if id <= 0 049 * @throws IllegalArgumentException if type is null 050 * 051 */ 052 protected History(long id, OsmPrimitiveType type, List<HistoryOsmPrimitive> versions) { 053 if (id <= 0) 054 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected, got {1}", "id", id)); 055 CheckParameterUtil.ensureParameterNotNull(type, "type"); 056 this.id = id; 057 this.type = type; 058 this.versions = new ArrayList<HistoryOsmPrimitive>(); 059 if (versions != null) { 060 this.versions.addAll(versions); 061 } 062 } 063 064 public History sortAscending() { 065 ArrayList<HistoryOsmPrimitive> copy = new ArrayList<HistoryOsmPrimitive>(versions); 066 Collections.sort( 067 copy, 068 new Comparator<HistoryOsmPrimitive>() { 069 public int compare(HistoryOsmPrimitive o1, HistoryOsmPrimitive o2) { 070 return o1.compareTo(o2); 071 } 072 } 073 ); 074 return new History(id, type, copy); 075 } 076 077 public History sortDescending() { 078 ArrayList<HistoryOsmPrimitive> copy = new ArrayList<HistoryOsmPrimitive>(versions); 079 Collections.sort( 080 copy, 081 new Comparator<HistoryOsmPrimitive>() { 082 public int compare(HistoryOsmPrimitive o1, HistoryOsmPrimitive o2) { 083 return o2.compareTo(o1); 084 } 085 } 086 ); 087 return new History(id, type,copy); 088 } 089 090 public History from(final Date fromDate) { 091 return filter( 092 this, 093 new FilterPredicate() { 094 public boolean matches(HistoryOsmPrimitive primitive) { 095 return primitive.getTimestamp().compareTo(fromDate) >= 0; 096 } 097 } 098 ); 099 } 100 101 public History until(final Date untilDate) { 102 return filter( 103 this, 104 new FilterPredicate() { 105 public boolean matches(HistoryOsmPrimitive primitive) { 106 return primitive.getTimestamp().compareTo(untilDate) <= 0; 107 } 108 } 109 ); 110 } 111 112 public History between(Date fromDate, Date untilDate) { 113 return this.from(fromDate).until(untilDate); 114 } 115 116 public History from(final long fromVersion) { 117 return filter( 118 this, 119 new FilterPredicate() { 120 public boolean matches(HistoryOsmPrimitive primitive) { 121 return primitive.getVersion() >= fromVersion; 122 } 123 } 124 ); 125 } 126 127 public History until(final long untilVersion) { 128 return filter( 129 this, 130 new FilterPredicate() { 131 public boolean matches(HistoryOsmPrimitive primitive) { 132 return primitive.getVersion() <= untilVersion; 133 } 134 } 135 ); 136 } 137 138 public History between(long fromVersion, long untilVersion) { 139 return this.from(fromVersion).until(untilVersion); 140 } 141 142 public History forUserId(final long uid) { 143 return filter( 144 this, 145 new FilterPredicate() { 146 public boolean matches(HistoryOsmPrimitive primitive) { 147 return primitive.getUser() != null && primitive.getUser().getId() == uid; 148 } 149 } 150 ); 151 } 152 153 public long getId() { 154 return id; 155 } 156 157 /** 158 * Replies the primitive id for this history. 159 * 160 * @return the primitive id 161 */ 162 public PrimitiveId getPrimitiveId() { 163 return new SimplePrimitiveId(id, type); 164 } 165 166 public boolean contains(long version){ 167 for (HistoryOsmPrimitive primitive: versions) { 168 if (primitive.matches(id,version)) 169 return true; 170 } 171 return false; 172 } 173 174 /** 175 * Replies the history primitive with version <code>version</code>. null, 176 * if no such primitive exists. 177 * 178 * @param version the version 179 * @return the history primitive with version <code>version</code> 180 */ 181 public HistoryOsmPrimitive getByVersion(long version) { 182 for (HistoryOsmPrimitive primitive: versions) { 183 if (primitive.matches(id,version)) 184 return primitive; 185 } 186 return null; 187 } 188 189 public HistoryOsmPrimitive getByDate(Date date) { 190 History h = sortAscending(); 191 192 if (h.versions.isEmpty()) 193 return null; 194 if (h.get(0).getTimestamp().compareTo(date)> 0) 195 return null; 196 for (int i = 1; i < h.versions.size();i++) { 197 if (h.get(i-1).getTimestamp().compareTo(date) <= 0 198 && h.get(i).getTimestamp().compareTo(date) >= 0) 199 return h.get(i); 200 } 201 return h.getLatest(); 202 } 203 204 public HistoryOsmPrimitive get(int idx) { 205 if (idx < 0 || idx >= versions.size()) 206 throw new IndexOutOfBoundsException(MessageFormat.format("Parameter ''{0}'' in range 0..{1} expected. Got ''{2}''.", "idx", versions.size()-1, idx)); 207 return versions.get(idx); 208 } 209 210 public HistoryOsmPrimitive getEarliest() { 211 if (isEmpty()) 212 return null; 213 return sortAscending().versions.get(0); 214 } 215 216 public HistoryOsmPrimitive getLatest() { 217 if (isEmpty()) 218 return null; 219 return sortDescending().versions.get(0); 220 } 221 222 public int getNumVersions() { 223 return versions.size(); 224 } 225 226 public boolean isEmpty() { 227 return versions.isEmpty(); 228 } 229 230 public OsmPrimitiveType getType() { 231 return type; 232 } 233 234 @Override 235 public String toString() { 236 String result = "History [" 237 + (type != null ? "type=" + type + ", " : "") + "id=" + id; 238 if (versions != null) { 239 result += ", versions=\n"; 240 for (HistoryOsmPrimitive v : versions) { 241 result += "\t" + v + ",\n"; 242 } 243 } 244 result += "]"; 245 return result; 246 } 247 }