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    static private AtomicLong uidCounter = new AtomicLong();
028
029    /**
030     * the map of known users
031     */
032    private static Map<Long,User> userMap = new HashMap<Long,User>();
033    private final static 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<User>();
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<String>();
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<String>(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        StringBuffer s = new StringBuffer();
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}