| 1 | // License: GPL. Copyright 2007 by Immanuel Scholz and others |
|---|
| 2 | package org.openstreetmap.josm.data.osm; |
|---|
| 3 | |
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
|---|
| 5 | |
|---|
| 6 | import java.io.BufferedReader; |
|---|
| 7 | import java.io.InputStreamReader; |
|---|
| 8 | import java.io.IOException; |
|---|
| 9 | |
|---|
| 10 | import java.util.ArrayList; |
|---|
| 11 | import java.util.concurrent.atomic.AtomicLong; |
|---|
| 12 | import java.util.HashMap; |
|---|
| 13 | import java.util.HashSet; |
|---|
| 14 | import java.util.List; |
|---|
| 15 | |
|---|
| 16 | import org.openstreetmap.josm.Main; |
|---|
| 17 | import org.openstreetmap.josm.io.MirroredInputStream; |
|---|
| 18 | import org.openstreetmap.josm.tools.Utils; |
|---|
| 19 | |
|---|
| 20 | /** |
|---|
| 21 | * A simple class to keep a list of user names. |
|---|
| 22 | * |
|---|
| 23 | * Instead of storing user names as strings with every OSM primitive, we store |
|---|
| 24 | * a reference to an user object, and make sure that for each username there |
|---|
| 25 | * is only one user object. |
|---|
| 26 | * |
|---|
| 27 | * |
|---|
| 28 | */ |
|---|
| 29 | public class User { |
|---|
| 30 | |
|---|
| 31 | static private AtomicLong uidCounter = new AtomicLong(); |
|---|
| 32 | |
|---|
| 33 | /** |
|---|
| 34 | * the map of known users |
|---|
| 35 | */ |
|---|
| 36 | private static HashMap<Long,User> userMap = new HashMap<Long,User>(); |
|---|
| 37 | private static HashSet<Long> relicensingUsers = null; |
|---|
| 38 | private static HashSet<Long> nonRelicensingUsers = null; |
|---|
| 39 | private final static User anonymous = createLocalUser(tr("<anonymous>")); |
|---|
| 40 | |
|---|
| 41 | private static long getNextLocalUid() { |
|---|
| 42 | return uidCounter.decrementAndGet(); |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | /** |
|---|
| 46 | * Creates a local user with the given name |
|---|
| 47 | * |
|---|
| 48 | * @param name the name |
|---|
| 49 | */ |
|---|
| 50 | public static User createLocalUser(String name) { |
|---|
| 51 | for(long i = -1; i >= uidCounter.get(); --i) |
|---|
| 52 | { |
|---|
| 53 | User olduser = getById(i); |
|---|
| 54 | if(olduser != null && olduser.hasName(name)) |
|---|
| 55 | return olduser; |
|---|
| 56 | } |
|---|
| 57 | User user = new User(getNextLocalUid(), name); |
|---|
| 58 | userMap.put(user.getId(), user); |
|---|
| 59 | return user; |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | /** |
|---|
| 63 | * Creates a user known to the OSM server |
|---|
| 64 | * |
|---|
| 65 | * @param uid the user id |
|---|
| 66 | * @param name the name |
|---|
| 67 | */ |
|---|
| 68 | public static User createOsmUser(long uid, String name) { |
|---|
| 69 | User user = userMap.get(uid); |
|---|
| 70 | if (user == null) { |
|---|
| 71 | user = new User(uid, name); |
|---|
| 72 | userMap.put(user.getId(), user); |
|---|
| 73 | } |
|---|
| 74 | if (name != null) user.addName(name); |
|---|
| 75 | return user; |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | /** |
|---|
| 79 | * clears the static map of user ids to user objects |
|---|
| 80 | * |
|---|
| 81 | */ |
|---|
| 82 | public static void clearUserMap() { |
|---|
| 83 | userMap.clear(); |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | /** |
|---|
| 87 | * Returns the user with user id <code>uid</code> or null if this user doesn't exist |
|---|
| 88 | * |
|---|
| 89 | * @param uid the user id |
|---|
| 90 | * @return the user; null, if there is no user with this id |
|---|
| 91 | */ |
|---|
| 92 | public static User getById(long uid) { |
|---|
| 93 | return userMap.get(uid); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | /** |
|---|
| 97 | * Returns the list of users with name <code>name</code> or the empty list if |
|---|
| 98 | * no such users exist |
|---|
| 99 | * |
|---|
| 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 | } |
|---|