001 // License: GPL. Copyright 2007 by Immanuel Scholz and others 002 package org.openstreetmap.josm.data.osm; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.io.BufferedReader; 007 import java.io.InputStreamReader; 008 import java.io.IOException; 009 010 import java.util.ArrayList; 011 import java.util.concurrent.atomic.AtomicLong; 012 import java.util.HashMap; 013 import java.util.HashSet; 014 import java.util.List; 015 016 import org.openstreetmap.josm.Main; 017 import org.openstreetmap.josm.io.MirroredInputStream; 018 import org.openstreetmap.josm.tools.Utils; 019 020 /** 021 * A simple class to keep a list of user names. 022 * 023 * Instead of storing user names as strings with every OSM primitive, we store 024 * a reference to an user object, and make sure that for each username there 025 * is only one user object. 026 * 027 * 028 */ 029 public class User { 030 031 static private AtomicLong uidCounter = new AtomicLong(); 032 033 /** 034 * the map of known users 035 */ 036 private static HashMap<Long,User> userMap = new HashMap<Long,User>(); 037 private static HashSet<Long> relicensingUsers = null; 038 private static HashSet<Long> nonRelicensingUsers = null; 039 private final static User anonymous = createLocalUser(tr("<anonymous>")); 040 041 private static long getNextLocalUid() { 042 return uidCounter.decrementAndGet(); 043 } 044 045 /** 046 * Creates a local user with the given name 047 * 048 * @param name the name 049 */ 050 public static User createLocalUser(String name) { 051 for(long i = -1; i >= uidCounter.get(); --i) 052 { 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 /** 063 * Creates a user known to the OSM server 064 * 065 * @param uid the user id 066 * @param name the name 067 */ 068 public static User createOsmUser(long uid, String name) { 069 User user = userMap.get(uid); 070 if (user == null) { 071 user = new User(uid, name); 072 userMap.put(user.getId(), user); 073 } 074 if (name != null) user.addName(name); 075 return user; 076 } 077 078 /** 079 * clears the static map of user ids to user objects 080 * 081 */ 082 public static void clearUserMap() { 083 userMap.clear(); 084 } 085 086 /** 087 * Returns the user with user id <code>uid</code> or null if this user doesn't exist 088 * 089 * @param uid the user id 090 * @return the user; null, if there is no user with this id 091 */ 092 public static User getById(long uid) { 093 return userMap.get(uid); 094 } 095 096 /** 097 * Returns the list of users with name <code>name</code> or the empty list if 098 * no such users exist 099 * 100 * @param name the user name 101 * @return the list of users with name <code>name</code> or the empty list if 102 * no such users exist 103 */ 104 public static List<User> getByName(String name) { 105 if (name == null) { 106 name = ""; 107 } 108 List<User> ret = new ArrayList<User>(); 109 for (User user: userMap.values()) { 110 if (user.hasName(name)) { 111 ret.add(user); 112 } 113 } 114 return ret; 115 } 116 117 public static User getAnonymous() { 118 return anonymous; 119 } 120 121 public static void initRelicensingInformation() { 122 if (relicensingUsers == null) { 123 loadRelicensingInformation(false); 124 } 125 } 126 127 public static void loadRelicensingInformation(boolean clean) { 128 relicensingUsers = new HashSet<Long>(); 129 nonRelicensingUsers = new HashSet<Long>(); 130 try { 131 MirroredInputStream stream = new MirroredInputStream( 132 Main.pref.get("url.licensechange", 133 "http://planet.openstreetmap.org/users_agreed/users_agreed.txt"), 134 clean ? 1 : 7200); 135 try { 136 InputStreamReader r; 137 r = new InputStreamReader(stream); 138 BufferedReader reader = new BufferedReader(r); 139 String line; 140 while ((line = reader.readLine()) != null) { 141 if (line.startsWith("#")) continue; 142 try { 143 Long id = new Long(Long.parseLong(line.trim())); 144 relicensingUsers.add(id); 145 } catch (java.lang.NumberFormatException ex) { 146 } 147 } 148 } 149 finally { 150 stream.close(); 151 } 152 } catch (IOException ex) { 153 } 154 155 try { 156 MirroredInputStream stream = new MirroredInputStream( 157 Main.pref.get("url.licensechange_reject", 158 "http://planet.openstreetmap.org/users_agreed/users_disagreed.txt"), 159 clean ? 1 : 7200); 160 try { 161 InputStreamReader r; 162 r = new InputStreamReader(stream); 163 BufferedReader reader = new BufferedReader(r); 164 String line; 165 while ((line = reader.readLine()) != null) { 166 if (line.startsWith("#")) continue; 167 try { 168 Long id = new Long(Long.parseLong(line.trim())); 169 nonRelicensingUsers.add(id); 170 } catch (java.lang.NumberFormatException ex) { 171 } 172 } 173 } 174 finally { 175 stream.close(); 176 } 177 } catch (IOException ex) { 178 } 179 } 180 181 /** the user name */ 182 private final HashSet<String> names = new HashSet<String>(); 183 /** the user id */ 184 private final long uid; 185 private int relicensingStatus = STATUS_UNKNOWN; 186 187 public static final int STATUS_UNKNOWN = -1; 188 public static final int STATUS_UNDECIDED = 0; 189 public static final int STATUS_AGREED = 1; 190 public static final int STATUS_NOT_AGREED = 2; 191 public static final int STATUS_AUTO_AGREED = 3; 192 public static final int STATUS_ANONYMOUS = 4; 193 194 /** 195 * Finds out this user's relicensing status and saves it for quicker 196 * access. 197 */ 198 public int getRelicensingStatus() { 199 if (relicensingStatus != STATUS_UNKNOWN) return relicensingStatus; 200 if (uid >= 286582) return (relicensingStatus = STATUS_AUTO_AGREED); 201 if (relicensingUsers == null) return STATUS_UNKNOWN; 202 Long id = new Long(uid); 203 if (relicensingUsers.contains(id)) return (relicensingStatus = STATUS_AGREED); 204 if (nonRelicensingUsers == null) return STATUS_UNKNOWN; 205 if (nonRelicensingUsers.contains(id)) return (relicensingStatus = STATUS_NOT_AGREED); 206 return STATUS_UNDECIDED; 207 } 208 209 /** 210 * Sets this user's relicensing status. This can be used if relicensing 211 * information is available from another source so that directly looking 212 * at the users_agreed/users_not_agreed files it not required. 213 */ 214 public void setRelicensingStatus(int status) { 215 relicensingStatus = status; 216 } 217 218 /** 219 * Replies the user name 220 * 221 * @return the user name. Never null, but may be the empty string 222 */ 223 public String getName() { 224 return Utils.join("/", names); 225 } 226 227 /** 228 * Returns the list of user names 229 * 230 * @returns list of names 231 */ 232 public ArrayList<String> getNames() { 233 return new ArrayList<String>(names); 234 } 235 236 /** 237 * Adds a user name to the list if it is not there, yet. 238 * 239 * @param name 240 */ 241 public void addName(String name) { 242 names.add(name); 243 } 244 245 /** 246 * Returns true if the name is in the names list 247 * 248 * @param name 249 */ 250 public boolean hasName(String name) { 251 return names.contains(name); 252 } 253 254 /** 255 * Replies the user id. If this user is known to the OSM server the positive user id 256 * from the server is replied. Otherwise, a negative local value is replied. 257 * 258 * A negative local is only unique during an editing session. It is lost when the 259 * application is closed and there is no guarantee that a negative local user id is 260 * always bound to a user with the same name. 261 * 262 */ 263 public long getId() { 264 return uid; 265 } 266 267 /** private constructor, only called from get method. */ 268 private User(long uid, String name) { 269 this.uid = uid; 270 if (name != null) { 271 addName(name); 272 } 273 } 274 275 public boolean isOsmUser() { 276 return uid > 0; 277 } 278 279 public boolean isLocalUser() { 280 return uid < 0; 281 } 282 283 @Override 284 public int hashCode() { 285 final int prime = 31; 286 int result = 1; 287 result = prime * result + getName().hashCode(); 288 result = prime * result + (int) (uid ^ (uid >>> 32)); 289 return result; 290 } 291 292 @Override 293 public boolean equals(Object obj) { 294 if (! (obj instanceof User)) 295 return false; 296 User other = (User) obj; 297 if (uid != other.uid) 298 return false; 299 return true; 300 } 301 302 @Override 303 public String toString() { 304 StringBuffer s = new StringBuffer(); 305 s.append("id:"+uid); 306 if (names.size() == 1) { 307 s.append(" name:"+getName()); 308 } 309 else if (names.size() > 1) { 310 s.append(String.format(" %d names:%s", names.size(), getName())); 311 } 312 return s.toString(); 313 } 314 }