001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.List; 010import java.util.Map; 011import java.util.Objects; 012import java.util.Set; 013 014import org.openstreetmap.josm.tools.Utils; 015 016/** 017 * A simple class to keep a list of user names. 018 * 019 * Instead of storing user names as strings with every OSM primitive, we store 020 * a reference to an user object, and make sure that for each username there 021 * is only one user object. 022 * 023 * @since 227 024 */ 025public final class User { 026 027 private static long uidCounter; 028 029 /** 030 * the map of known users 031 */ 032 private static Map<Long, User> userMap = new HashMap<>(); 033 034 /** 035 * The anonymous user is a local user used in places where no user is known. 036 * @see #getAnonymous() 037 */ 038 private static final User anonymous = createLocalUser(tr("<anonymous>")); 039 040 private static long getNextLocalUid() { 041 uidCounter--; 042 return uidCounter; 043 } 044 045 /** 046 * Creates a local user with the given name 047 * 048 * @param name the name 049 * @return a new local user with the given name 050 */ 051 public static synchronized User createLocalUser(String name) { 052 for (long i = -1; i >= uidCounter; --i) { 053 User olduser = getById(i); 054 if (olduser != null && olduser.hasName(name)) 055 return olduser; 056 } 057 User user = new User(getNextLocalUid(), name); 058 userMap.put(user.getId(), user); 059 return user; 060 } 061 062 private static User lastUser; 063 064 /** 065 * Creates a user known to the OSM server 066 * 067 * @param uid the user id 068 * @param name the name 069 * @return a new OSM user with the given name and uid 070 */ 071 public static synchronized User createOsmUser(long uid, String name) { 072 073 if (lastUser != null && lastUser.getId() == uid) { 074 return lastUser; 075 } 076 077 Long ouid = uid; 078 User user = userMap.get(ouid); 079 if (user == null) { 080 user = new User(uid, name); 081 userMap.put(ouid, user); 082 } 083 if (name != null) user.addName(name); 084 085 lastUser = user; 086 087 return user; 088 } 089 090 /** 091 * clears the static map of user ids to user objects 092 */ 093 public static synchronized void clearUserMap() { 094 userMap.clear(); 095 } 096 097 /** 098 * Returns the user with user id <code>uid</code> or null if this user doesn't exist 099 * 100 * @param uid the user id 101 * @return the user; null, if there is no user with this id 102 */ 103 public static synchronized User getById(long uid) { 104 return userMap.get(uid); 105 } 106 107 /** 108 * Returns the list of users with name <code>name</code> or the empty list if 109 * no such users exist 110 * 111 * @param name the user name 112 * @return the list of users with name <code>name</code> or the empty list if 113 * no such users exist 114 */ 115 public static synchronized List<User> getByName(String name) { 116 if (name == null) { 117 name = ""; 118 } 119 List<User> ret = new ArrayList<>(); 120 for (User user: userMap.values()) { 121 if (user.hasName(name)) { 122 ret.add(user); 123 } 124 } 125 return ret; 126 } 127 128 /** 129 * Replies the anonymous user 130 * @return The anonymous user 131 */ 132 public static User getAnonymous() { 133 return anonymous; 134 } 135 136 /** the user name */ 137 private final Set<String> names = new HashSet<>(); 138 /** the user id */ 139 private final long uid; 140 141 /** 142 * Replies the user name 143 * 144 * @return the user name. Never <code>null</code>, but may be the empty string 145 */ 146 public String getName() { 147 return Utils.join("/", names); 148 } 149 150 /** 151 * Returns the list of user names 152 * 153 * @return list of names 154 */ 155 public List<String> getNames() { 156 return new ArrayList<>(names); 157 } 158 159 /** 160 * Adds a user name to the list if it is not there, yet. 161 * 162 * @param name User name 163 */ 164 public void addName(String name) { 165 names.add(name); 166 } 167 168 /** 169 * Returns true if the name is in the names list 170 * 171 * @param name User name 172 * @return <code>true</code> if the name is in the names list 173 */ 174 public boolean hasName(String name) { 175 return names.contains(name); 176 } 177 178 /** 179 * Replies the user id. If this user is known to the OSM server the positive user id 180 * from the server is replied. Otherwise, a negative local value is replied. 181 * 182 * A negative local is only unique during an editing session. It is lost when the 183 * application is closed and there is no guarantee that a negative local user id is 184 * always bound to a user with the same name. 185 * 186 * @return the user id 187 */ 188 public long getId() { 189 return uid; 190 } 191 192 /** 193 * Private constructor, only called from get method. 194 * @param uid user id 195 * @param name user name 196 */ 197 private User(long uid, String name) { 198 this.uid = uid; 199 if (name != null) { 200 addName(name); 201 } 202 } 203 204 /** 205 * Determines if this user is known to OSM 206 * @return {@code true} if this user is known to OSM, {@code false} otherwise 207 */ 208 public boolean isOsmUser() { 209 return uid > 0; 210 } 211 212 /** 213 * Determines if this user is local 214 * @return {@code true} if this user is local, {@code false} otherwise 215 */ 216 public boolean isLocalUser() { 217 return uid < 0; 218 } 219 220 @Override 221 public int hashCode() { 222 return Objects.hash(uid); 223 } 224 225 @Override 226 public boolean equals(Object obj) { 227 if (this == obj) return true; 228 if (obj == null || getClass() != obj.getClass()) return false; 229 User user = (User) obj; 230 return uid == user.uid; 231 } 232 233 @Override 234 public String toString() { 235 StringBuilder s = new StringBuilder(); 236 s.append("id:").append(uid); 237 if (names.size() == 1) { 238 s.append(" name:").append(getName()); 239 } else if (names.size() > 1) { 240 s.append(String.format(" %d names:%s", names.size(), getName())); 241 } 242 return s.toString(); 243 } 244}