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 }