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    }