source: josm/trunk/src/org/openstreetmap/josm/data/osm/User.java@ 13100

Last change on this file since 13100 was 13100, checked in by Don-vip, 6 years ago

see #15534 - ensure user names are never null, as expected

  • Property svn:eol-style set to native
File size: 7.1 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[227]2package org.openstreetmap.josm.data.osm;
3
[4602]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[2070]6import java.util.ArrayList;
[10841]7import java.util.Collection;
[227]8import java.util.HashMap;
[10841]9import java.util.LinkedHashSet;
[2070]10import java.util.List;
[6317]11import java.util.Map;
[9371]12import java.util.Objects;
[4025]13
[1169]14/**
[227]15 * A simple class to keep a list of user names.
[1169]16 *
[655]17 * Instead of storing user names as strings with every OSM primitive, we store
[227]18 * a reference to an user object, and make sure that for each username there
19 * is only one user object.
[1169]20 *
[8588]21 * @since 227
[227]22 */
[6362]23public final class User {
[2471]24
[8840]25 private static long uidCounter;
[2471]26
[2070]27 /**
28 * the map of known users
29 */
[8510]30 private static Map<Long, User> userMap = new HashMap<>();
[8155]31
32 /**
33 * The anonymous user is a local user used in places where no user is known.
34 * @see #getAnonymous()
35 */
[12537]36 private static final User ANONYMOUS = createLocalUser(tr("<anonymous>"));
[227]37
[2070]38 private static long getNextLocalUid() {
[8155]39 uidCounter--;
40 return uidCounter;
[2070]41 }
[1677]42
[2070]43 /**
44 * Creates a local user with the given name
[2512]45 *
[2070]46 * @param name the name
[6223]47 * @return a new local user with the given name
[2070]48 */
[8155]49 public static synchronized User createLocalUser(String name) {
[8510]50 for (long i = -1; i >= uidCounter; --i) {
[8155]51 User olduser = getById(i);
[8510]52 if (olduser != null && olduser.hasName(name))
[8155]53 return olduser;
[3505]54 }
[2070]55 User user = new User(getNextLocalUid(), name);
56 userMap.put(user.getId(), user);
57 return user;
58 }
[1169]59
[8840]60 private static User lastUser;
[8588]61
[2070]62 /**
63 * Creates a user known to the OSM server
[2512]64 *
[2070]65 * @param uid the user id
66 * @param name the name
[6223]67 * @return a new OSM user with the given name and uid
[2070]68 */
[8155]69 public static synchronized User createOsmUser(long uid, String name) {
[8588]70
71 if (lastUser != null && lastUser.getId() == uid) {
[11909]72 lastUser.setPreferredName(name);
[8588]73 return lastUser;
74 }
75
[12865]76 User user = userMap.computeIfAbsent(uid, k -> new User(uid, name));
[4066]77 if (name != null) user.addName(name);
[8588]78
79 lastUser = user;
80
[2070]81 return user;
82 }
83
[2471]84 /**
85 * clears the static map of user ids to user objects
86 */
[8155]87 public static synchronized void clearUserMap() {
[2471]88 userMap.clear();
[11912]89 lastUser = null;
[2471]90 }
[2070]91
92 /**
93 * Returns the user with user id <code>uid</code> or null if this user doesn't exist
[2512]94 *
[2070]95 * @param uid the user id
96 * @return the user; null, if there is no user with this id
97 */
[8155]98 public static synchronized User getById(long uid) {
[2070]99 return userMap.get(uid);
100 }
101
102 /**
103 * Returns the list of users with name <code>name</code> or the empty list if
104 * no such users exist
[2512]105 *
[2070]106 * @param name the user name
107 * @return the list of users with name <code>name</code> or the empty list if
108 * no such users exist
109 */
[8155]110 public static synchronized List<User> getByName(String name) {
[2284]111 if (name == null) {
112 name = "";
113 }
[7005]114 List<User> ret = new ArrayList<>();
[2070]115 for (User user: userMap.values()) {
[2638]116 if (user.hasName(name)) {
[2070]117 ret.add(user);
118 }
119 }
120 return ret;
121 }
122
[6223]123 /**
124 * Replies the anonymous user
125 * @return The anonymous user
126 */
[4602]127 public static User getAnonymous() {
[12537]128 return ANONYMOUS;
[4602]129 }
130
[2070]131 /** the user name */
[10841]132 private final LinkedHashSet<String> names = new LinkedHashSet<>();
[2070]133 /** the user id */
[2284]134 private final long uid;
[2070]135
[4602]136 /**
[2070]137 * Replies the user name
[2512]138 *
[5818]139 * @return the user name. Never <code>null</code>, but may be the empty string
[10841]140 * @see #getByName(String)
141 * @see #createOsmUser(long, String)
142 * @see #createLocalUser(String)
[2070]143 */
144 public String getName() {
[10841]145 return names.isEmpty() ? "" : names.iterator().next();
[2070]146 }
147
[2863]148 /**
[2638]149 * Returns the list of user names
[2711]150 *
[5818]151 * @return list of names
[2638]152 */
[6316]153 public List<String> getNames() {
[7005]154 return new ArrayList<>(names);
[2638]155 }
156
[2863]157 /**
[2638]158 * Adds a user name to the list if it is not there, yet.
[2711]159 *
[8470]160 * @param name User name
[13100]161 * @throws NullPointerException if name is null
[2638]162 */
163 public void addName(String name) {
[13100]164 names.add(Objects.requireNonNull(name, "name"));
[2638]165 }
166
[2863]167 /**
[10841]168 * Sets the preferred user name, i.e., the one that will be returned when calling {@link #getName()}.
169 *
170 * Rationale: A user can change its name multiple times and after reading various (outdated w.r.t. user name)
171 * data files it is unclear which is the up-to-date user name.
172 * @param name the preferred user name to set
[13100]173 * @throws NullPointerException if name is null
[10841]174 */
175 public void setPreferredName(String name) {
176 if (names.size() == 1 && names.contains(name)) {
177 return;
178 }
179 final Collection<String> allNames = new LinkedHashSet<>(names);
180 names.clear();
[13100]181 names.add(Objects.requireNonNull(name, "name"));
[10841]182 names.addAll(allNames);
183 }
184
185 /**
[2638]186 * Returns true if the name is in the names list
[2711]187 *
[8470]188 * @param name User name
[5818]189 * @return <code>true</code> if the name is in the names list
[2638]190 */
191 public boolean hasName(String name) {
192 return names.contains(name);
193 }
194
[2070]195 /**
196 * Replies the user id. If this user is known to the OSM server the positive user id
197 * from the server is replied. Otherwise, a negative local value is replied.
[2512]198 *
[2070]199 * A negative local is only unique during an editing session. It is lost when the
200 * application is closed and there is no guarantee that a negative local user id is
201 * always bound to a user with the same name.
[2512]202 *
[5818]203 * @return the user id
[2070]204 */
205 public long getId() {
206 return uid;
207 }
208
[9243]209 /**
210 * Private constructor, only called from get method.
211 * @param uid user id
212 * @param name user name
213 */
[2070]214 private User(long uid, String name) {
215 this.uid = uid;
[2638]216 if (name != null) {
217 addName(name);
[2284]218 }
[1169]219 }
220
[6223]221 /**
222 * Determines if this user is known to OSM
223 * @return {@code true} if this user is known to OSM, {@code false} otherwise
224 */
[2070]225 public boolean isOsmUser() {
226 return uid > 0;
[1169]227 }
[2034]228
[6223]229 /**
230 * Determines if this user is local
231 * @return {@code true} if this user is local, {@code false} otherwise
232 */
[2070]233 public boolean isLocalUser() {
234 return uid < 0;
235 }
236
[2034]237 @Override
238 public int hashCode() {
[9371]239 return Objects.hash(uid);
[2034]240 }
241
242 @Override
243 public boolean equals(Object obj) {
[9371]244 if (this == obj) return true;
245 if (obj == null || getClass() != obj.getClass()) return false;
246 User user = (User) obj;
247 return uid == user.uid;
[2034]248 }
[3262]249
250 @Override
251 public String toString() {
[6822]252 StringBuilder s = new StringBuilder();
[6223]253 s.append("id:").append(uid);
[3262]254 if (names.size() == 1) {
[6223]255 s.append(" name:").append(getName());
[8342]256 } else if (names.size() > 1) {
[3262]257 s.append(String.format(" %d names:%s", names.size(), getName()));
258 }
259 return s.toString();
260 }
[227]261}
Note: See TracBrowser for help on using the repository browser.