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

Last change on this file since 10841 was 10841, checked in by simon04, 8 years ago

fix #8251 fix #10922 - Improve handling of user names that have changed

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