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