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