001    // License: GPL. Copyright 2007 by Martijn van Oosterhout and others
002    package org.openstreetmap.josm.data.osm;
003    
004    import java.util.Collection;
005    import java.util.Date;
006    import java.util.HashMap;
007    import java.util.Map;
008    
009    import org.openstreetmap.josm.data.Bounds;
010    import org.openstreetmap.josm.data.coor.LatLon;
011    import org.openstreetmap.josm.data.osm.visitor.Visitor;
012    
013    /**
014     * Represents a single changeset in JOSM. For now its only used during
015     * upload but in the future we may do more.
016     *
017     */
018    public final class Changeset implements Tagged {
019        
020        /** The maximum changeset comment text length allowed by API 0.6 **/
021        public static final int MAX_COMMENT_LENGTH = 255;
022        
023        /** the changeset id */
024        private int id;
025        /** the user who owns the changeset */
026        private User user;
027        /** date this changeset was created at */
028        private Date createdAt;
029        /** the date this changeset was closed at*/
030        private Date closedAt;
031        /** indicates whether this changeset is still open or not */
032        private boolean open;
033        /** the min. coordinates of the bounding box of this changeset */
034        private LatLon min;
035        /** the max. coordinates of the bounding box of this changeset */
036        private LatLon max;
037        /** the map of tags */
038        private Map<String,String> tags;
039        /** indicates whether this changeset is incomplete. For an
040         * incomplete changeset we only know its id
041         */
042        private boolean incomplete;
043        /** the changeset content */
044        private ChangesetDataSet content = null;
045    
046        /**
047         * Creates a new changeset with id 0.
048         */
049        public Changeset() {
050            this.id = 0;
051            this.tags = new HashMap<String, String>();
052        }
053    
054        /**
055         * Creates a changeset with id <code>id</code>. If id > 0, sets incomplete to true.
056         *
057         * @param id the id
058         */
059        public Changeset(int id) {
060            this.id = id;
061            this.incomplete = id > 0;
062            this.tags = new HashMap<String, String>();
063        }
064    
065        /**
066         * Creates a clone of <code>other</code>
067         *
068         * @param other the other changeset. If null, creates a new changeset with id 0.
069         */
070        public Changeset(Changeset other) {
071            if (other == null) {
072                this.id = 0;
073                this.tags = new HashMap<String, String>();
074            } else if (other.isIncomplete()) {
075                setId(other.getId());
076                this.incomplete = true;
077                this.tags = new HashMap<String, String>();
078            } else {
079                this.id = other.id;
080                mergeFrom(other);
081                this.incomplete = false;
082            }
083        }
084    
085        public void visit(Visitor v) {
086            v.visit(this);
087        }
088    
089        public int compareTo(Changeset other) {
090            return Integer.valueOf(getId()).compareTo(other.getId());
091        }
092    
093        public String getName() {
094            // no translation
095            return "changeset " + getId();
096        }
097    
098        public String getDisplayName(NameFormatter formatter) {
099            return formatter.format(this);
100        }
101    
102        public int getId() {
103            return id;
104        }
105    
106        public void setId(int id) {
107            this.id = id;
108        }
109    
110        public User getUser() {
111            return user;
112        }
113    
114        public void setUser(User user) {
115            this.user = user;
116        }
117    
118        public Date getCreatedAt() {
119            return createdAt;
120        }
121    
122        public void setCreatedAt(Date createdAt) {
123            this.createdAt = createdAt;
124        }
125    
126        public Date getClosedAt() {
127            return closedAt;
128        }
129    
130        public void setClosedAt(Date closedAt) {
131            this.closedAt = closedAt;
132        }
133    
134        public boolean isOpen() {
135            return open;
136        }
137    
138        public void setOpen(boolean open) {
139            this.open = open;
140        }
141    
142        public LatLon getMin() {
143            return min;
144        }
145    
146        public void setMin(LatLon min) {
147            this.min = min;
148        }
149    
150        public LatLon getMax() {
151            return max;
152        }
153    
154        public Bounds getBounds() {
155            if (min != null && max != null)
156                return new Bounds(min,max);
157            return null;
158        }
159    
160        public void setMax(LatLon max) {
161            this.max = max;
162        }
163    
164        public Map<String, String> getKeys() {
165            return tags;
166        }
167    
168        public void setKeys(Map<String, String> keys) {
169            this.tags = keys;
170        }
171    
172        public boolean isIncomplete() {
173            return incomplete;
174        }
175    
176        public void setIncomplete(boolean incomplete) {
177            this.incomplete = incomplete;
178        }
179    
180        public void put(String key, String value) {
181            this.tags.put(key, value);
182        }
183    
184        public String get(String key) {
185            return this.tags.get(key);
186        }
187    
188        public void remove(String key) {
189            this.tags.remove(key);
190        }
191    
192        public void removeAll() {
193            this.tags.clear();
194        }
195    
196        public boolean hasEqualSemanticAttributes(Changeset other) {
197            if (other == null)
198                return false;
199            if (closedAt == null) {
200                if (other.closedAt != null)
201                    return false;
202            } else if (!closedAt.equals(other.closedAt))
203                return false;
204            if (createdAt == null) {
205                if (other.createdAt != null)
206                    return false;
207            } else if (!createdAt.equals(other.createdAt))
208                return false;
209            if (id != other.id)
210                return false;
211            if (max == null) {
212                if (other.max != null)
213                    return false;
214            } else if (!max.equals(other.max))
215                return false;
216            if (min == null) {
217                if (other.min != null)
218                    return false;
219            } else if (!min.equals(other.min))
220                return false;
221            if (open != other.open)
222                return false;
223            if (tags == null) {
224                if (other.tags != null)
225                    return false;
226            } else if (!tags.equals(other.tags))
227                return false;
228            if (user == null) {
229                if (other.user != null)
230                    return false;
231            } else if (!user.equals(other.user))
232                return false;
233            return true;
234        }
235    
236        @Override
237        public int hashCode() {
238            if (id > 0)
239                return id;
240            else
241                return super.hashCode();
242        }
243    
244        @Override
245        public boolean equals(Object obj) {
246            if (this == obj)
247                return true;
248            if (obj == null)
249                return false;
250            if (getClass() != obj.getClass())
251                return false;
252            Changeset other = (Changeset) obj;
253            if (this.id > 0 && other.id == this.id)
254                return true;
255            return this == obj;
256        }
257    
258        public boolean hasKeys() {
259            return !tags.keySet().isEmpty();
260        }
261    
262        public Collection<String> keySet() {
263            return tags.keySet();
264        }
265    
266        public boolean isNew() {
267            return id <= 0;
268        }
269    
270        public void mergeFrom(Changeset other) {
271            if (other == null)
272                return;
273            if (id != other.id)
274                return;
275            this.user = other.user;
276            this.createdAt = other.createdAt;
277            this.closedAt = other.closedAt;
278            this.open  = other.open;
279            this.min = other.min;
280            this.max = other.max;
281            this.tags = new HashMap<String, String>(other.tags);
282            this.incomplete = other.incomplete;
283    
284            // FIXME: merging of content required?
285            this.content = other.content;
286        }
287    
288        public boolean hasContent() {
289            return content != null;
290        }
291    
292        public ChangesetDataSet getContent() {
293            return content;
294        }
295    
296        public void setContent(ChangesetDataSet content) {
297            this.content = content;
298        }
299    }