001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm.history; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.text.MessageFormat; 007import java.util.Collections; 008import java.util.Date; 009import java.util.HashMap; 010import java.util.Locale; 011import java.util.Map; 012import java.util.Objects; 013 014import org.openstreetmap.josm.data.osm.Changeset; 015import org.openstreetmap.josm.data.osm.Node; 016import org.openstreetmap.josm.data.osm.OsmPrimitive; 017import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 018import org.openstreetmap.josm.data.osm.PrimitiveId; 019import org.openstreetmap.josm.data.osm.Relation; 020import org.openstreetmap.josm.data.osm.SimplePrimitiveId; 021import org.openstreetmap.josm.data.osm.User; 022import org.openstreetmap.josm.data.osm.Way; 023import org.openstreetmap.josm.tools.CheckParameterUtil; 024 025/** 026 * Represents an immutable OSM primitive in the context of a historical view on 027 * OSM data. 028 * 029 */ 030public abstract class HistoryOsmPrimitive implements Comparable<HistoryOsmPrimitive> { 031 032 private long id; 033 private boolean visible; 034 private User user; 035 private long changesetId; 036 private Changeset changeset; 037 private Date timestamp; 038 private long version; 039 private Map<String, String> tags; 040 041 protected final void ensurePositiveLong(long value, String name) { 042 if (value <= 0) { 043 throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' > 0 expected. Got ''{1}''.", name, value)); 044 } 045 } 046 047 /** 048 * Constructs a new {@code HistoryOsmPrimitive}. 049 * 050 * @param id the id (> 0 required) 051 * @param version the version (> 0 required) 052 * @param visible whether the primitive is still visible 053 * @param user the user (!= null required) 054 * @param changesetId the changeset id (> 0 required) 055 * @param timestamp the timestamp (!= null required) 056 * 057 * @throws IllegalArgumentException if preconditions are violated 058 */ 059 public HistoryOsmPrimitive(long id, long version, boolean visible, User user, long changesetId, Date timestamp) { 060 this(id, version, visible, user, changesetId, timestamp, true); 061 } 062 063 /** 064 * Constructs a new {@code HistoryOsmPrimitive} with a configurable checking of historic parameters. 065 * This is needed to build virtual HistoryOsmPrimitives for modified primitives, which do not have a timestamp and a changeset id. 066 * 067 * @param id the id (> 0 required) 068 * @param version the version (> 0 required) 069 * @param visible whether the primitive is still visible 070 * @param user the user (!= null required) 071 * @param changesetId the changeset id (> 0 required if {@code checkHistoricParams} is true) 072 * @param timestamp the timestamp (!= null required if {@code checkHistoricParams} is true) 073 * @param checkHistoricParams if true, checks values of {@code changesetId} and {@code timestamp} 074 * 075 * @throws IllegalArgumentException if preconditions are violated 076 * @since 5440 077 */ 078 public HistoryOsmPrimitive(long id, long version, boolean visible, User user, long changesetId, Date timestamp, 079 boolean checkHistoricParams) { 080 ensurePositiveLong(id, "id"); 081 ensurePositiveLong(version, "version"); 082 CheckParameterUtil.ensureParameterNotNull(user, "user"); 083 if (checkHistoricParams) { 084 ensurePositiveLong(changesetId, "changesetId"); 085 CheckParameterUtil.ensureParameterNotNull(timestamp, "timestamp"); 086 } 087 this.id = id; 088 this.version = version; 089 this.visible = visible; 090 this.user = user; 091 this.changesetId = changesetId; 092 this.timestamp = timestamp; 093 tags = new HashMap<>(); 094 } 095 096 /** 097 * Constructs a new {@code HistoryOsmPrimitive} from an existing {@link OsmPrimitive}. 098 * @param p the primitive 099 */ 100 public HistoryOsmPrimitive(OsmPrimitive p) { 101 this(p.getId(), p.getVersion(), p.isVisible(), p.getUser(), p.getChangesetId(), p.getTimestamp()); 102 } 103 104 /** 105 * Replies a new {@link HistoryNode}, {@link HistoryWay} or {@link HistoryRelation} from an existing {@link OsmPrimitive}. 106 * @param p the primitive 107 * @return a new {@code HistoryNode}, {@code HistoryWay} or {@code HistoryRelation} from {@code p}. 108 */ 109 public static HistoryOsmPrimitive forOsmPrimitive(OsmPrimitive p) { 110 if (p instanceof Node) { 111 return new HistoryNode((Node) p); 112 } else if (p instanceof Way) { 113 return new HistoryWay((Way) p); 114 } else if (p instanceof Relation) { 115 return new HistoryRelation((Relation) p); 116 } else { 117 return null; 118 } 119 } 120 121 public long getId() { 122 return id; 123 } 124 125 public PrimitiveId getPrimitiveId() { 126 return new SimplePrimitiveId(id, getType()); 127 } 128 129 public boolean isVisible() { 130 return visible; 131 } 132 133 public User getUser() { 134 return user; 135 } 136 137 public long getChangesetId() { 138 return changesetId; 139 } 140 141 public Date getTimestamp() { 142 return timestamp; 143 } 144 145 public long getVersion() { 146 return version; 147 } 148 149 public boolean matches(long id, long version) { 150 return this.id == id && this.version == version; 151 } 152 153 public boolean matches(long id) { 154 return this.id == id; 155 } 156 157 public abstract OsmPrimitiveType getType(); 158 159 @Override 160 public int compareTo(HistoryOsmPrimitive o) { 161 if (this.id != o.id) 162 throw new ClassCastException(tr("Cannot compare primitive with ID ''{0}'' to primitive with ID ''{1}''.", o.id, this.id)); 163 return Long.compare(this.version, o.version); 164 } 165 166 public void put(String key, String value) { 167 tags.put(key, value); 168 } 169 170 public String get(String key) { 171 return tags.get(key); 172 } 173 174 public boolean hasTag(String key) { 175 return tags.get(key) != null; 176 } 177 178 public Map<String, String> getTags() { 179 return Collections.unmodifiableMap(tags); 180 } 181 182 public Changeset getChangeset() { 183 return changeset; 184 } 185 186 public void setChangeset(Changeset changeset) { 187 this.changeset = changeset; 188 } 189 190 /** 191 * Sets the tags for this history primitive. Removes all 192 * tags if <code>tags</code> is null. 193 * 194 * @param tags the tags. May be null. 195 */ 196 public void setTags(Map<String, String> tags) { 197 if (tags == null) { 198 this.tags = new HashMap<>(); 199 } else { 200 this.tags = new HashMap<>(tags); 201 } 202 } 203 204 /** 205 * Replies the name of this primitive. The default implementation replies the value 206 * of the tag <tt>name</tt> or null, if this tag is not present. 207 * 208 * @return the name of this primitive 209 */ 210 public String getName() { 211 if (get("name") != null) 212 return get("name"); 213 return null; 214 } 215 216 /** 217 * Replies the display name of a primitive formatted by <code>formatter</code> 218 * @param formatter The formatter used to generate a display name 219 * 220 * @return the display name 221 */ 222 public abstract String getDisplayName(HistoryNameFormatter formatter); 223 224 /** 225 * Replies the a localized name for this primitive given by the value of the tags (in this order) 226 * <ul> 227 * <li>name:lang_COUNTRY_Variant of the current locale</li> 228 * <li>name:lang_COUNTRY of the current locale</li> 229 * <li>name:lang of the current locale</li> 230 * <li>name of the current locale</li> 231 * </ul> 232 * 233 * null, if no such tag exists 234 * 235 * @return the name of this primitive 236 */ 237 public String getLocalName() { 238 String key = "name:" + Locale.getDefault(); 239 if (get(key) != null) 240 return get(key); 241 key = "name:" + Locale.getDefault().getLanguage() + '_' + Locale.getDefault().getCountry(); 242 if (get(key) != null) 243 return get(key); 244 key = "name:" + Locale.getDefault().getLanguage(); 245 if (get(key) != null) 246 return get(key); 247 return getName(); 248 } 249 250 @Override 251 public int hashCode() { 252 return Objects.hash(id, version); 253 } 254 255 @Override 256 public boolean equals(Object obj) { 257 if (this == obj) return true; 258 if (obj == null || getClass() != obj.getClass()) return false; 259 HistoryOsmPrimitive that = (HistoryOsmPrimitive) obj; 260 return id == that.id && 261 version == that.version; 262 } 263 264 @Override 265 public String toString() { 266 return getClass().getSimpleName() + " [version=" + version + ", id=" + id + ", visible=" + visible + ", " 267 + (timestamp != null ? "timestamp=" + timestamp : "") + ", " 268 + (user != null ? "user=" + user + ", " : "") + "changesetId=" 269 + changesetId 270 + ']'; 271 } 272}