001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.notes;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.ArrayList;
007import java.util.Comparator;
008import java.util.Date;
009import java.util.List;
010import java.util.Objects;
011
012import org.openstreetmap.josm.data.coor.LatLon;
013import org.openstreetmap.josm.tools.date.DateUtils;
014
015/**
016 * A map note. It always has at least one comment since a comment is required to create a note on osm.org.
017 * @since 7451
018 */
019public class Note {
020
021    /** Note state */
022    public enum State {
023        /** Note is open */
024        OPEN,
025        /** Note is closed */
026        CLOSED
027    }
028
029    /**
030     * Sorts notes in the following order:
031     * 1) Open notes
032     * 2) Closed notes
033     * 3) New notes
034     * Within each subgroup it sorts by ID
035     */
036    public static final Comparator<Note> DEFAULT_COMPARATOR = (n1, n2) -> {
037        if (n1.getId() < 0 && n2.getId() > 0) {
038            return 1;
039        }
040        if (n1.getId() > 0 && n2.getId() < 0) {
041            return -1;
042        }
043        if (n1.getState() == State.CLOSED && n2.getState() == State.OPEN) {
044            return 1;
045        }
046        if (n1.getState() == State.OPEN && n2.getState() == State.CLOSED) {
047            return -1;
048        }
049        return Long.compare(Math.abs(n1.getId()), Math.abs(n2.getId()));
050    };
051
052    /** Sorts notes strictly by creation date */
053    public static final Comparator<Note> DATE_COMPARATOR = (n1, n2) -> n1.createdAt.compareTo(n2.createdAt);
054
055    /** Sorts notes by user, then creation date */
056    public static final Comparator<Note> USER_COMPARATOR = (n1, n2) -> {
057        String n1User = n1.getFirstComment().getUser().getName();
058        String n2User = n2.getFirstComment().getUser().getName();
059        return n1User.equals(n2User) ? DATE_COMPARATOR.compare(n1, n2) : n1User.compareTo(n2User);
060    };
061
062    /** Sorts notes by the last modified date */
063    public static final Comparator<Note> LAST_ACTION_COMPARATOR =
064            (n1, n2) -> NoteComment.DATE_COMPARATOR.compare(n1.getLastComment(), n2.getLastComment());
065
066    private long id;
067    private LatLon latLon;
068    private Date createdAt;
069    private Date closedAt;
070    private State state;
071    private List<NoteComment> comments = new ArrayList<>();
072
073    /**
074     * Create a note with a given location
075     * @param latLon Geographic location of this note
076     */
077    public Note(LatLon latLon) {
078        this.latLon = latLon;
079    }
080
081    /**
082     * Returns the unique OSM ID of this note.
083     * @return The unique OSM ID of this note
084     */
085    public long getId() {
086        return id;
087    }
088
089    /**
090     * Sets note id.
091     * @param id OSM ID of this note
092     */
093    public void setId(long id) {
094        this.id = id;
095    }
096
097    /**
098     * Returns the geographic location of the note.
099     * @return The geographic location of the note
100     */
101    public LatLon getLatLon() {
102        return latLon;
103    }
104
105    /**
106     * Returns the date at which this note was submitted.
107     * @return Date that this note was submitted
108     */
109    public Date getCreatedAt() {
110        return DateUtils.cloneDate(createdAt);
111    }
112
113    /**
114     * Sets date at which this note has been created.
115     * @param createdAt date at which this note has been created
116     */
117    public void setCreatedAt(Date createdAt) {
118        this.createdAt = DateUtils.cloneDate(createdAt);
119    }
120
121    /**
122     * Returns the date at which this note was closed.
123     * @return Date that this note was closed. Null if it is still open.
124     */
125    public Date getClosedAt() {
126        return DateUtils.cloneDate(closedAt);
127    }
128
129    /**
130     * Sets date at which this note has been closed.
131     * @param closedAt date at which this note has been closed
132     */
133    public void setClosedAt(Date closedAt) {
134        this.closedAt = DateUtils.cloneDate(closedAt);
135    }
136
137    /**
138     * Returns the open or closed state of this note.
139     * @return The open or closed state of this note
140     */
141    public State getState() {
142        return state;
143    }
144
145    /**
146     * Sets the note state.
147     * @param state note state (open or closed)
148     */
149    public void setState(State state) {
150        this.state = state;
151    }
152
153    /**
154     * Returns the list of comments associated with this note.
155     * @return An ordered list of comments associated with this note
156     */
157    public List<NoteComment> getComments() {
158        return comments;
159    }
160
161    /**
162     * Returns the last comment, or {@code null}.
163     * @return the last comment, or {@code null}
164     * @since 11821
165     */
166    public NoteComment getLastComment() {
167        return comments.isEmpty() ? null : comments.get(comments.size()-1);
168    }
169
170    /**
171     * Adds a comment.
172     * @param comment note comment
173     */
174    public void addComment(NoteComment comment) {
175        comments.add(comment);
176    }
177
178    /**
179     * Returns the comment that was submitted by the user when creating the note
180     * @return First comment object
181     */
182    public NoteComment getFirstComment() {
183        return comments.isEmpty() ? null : comments.get(0);
184    }
185
186    /**
187     * Copies values from a new note into an existing one. Used after a note
188     * has been updated on the server and the local copy needs refreshing.
189     * @param note New values to copy
190     */
191    public void updateWith(Note note) {
192        this.comments = note.comments;
193        this.createdAt = DateUtils.cloneDate(note.createdAt);
194        this.id = note.id;
195        this.state = note.state;
196        this.latLon = note.latLon;
197    }
198
199    @Override
200    public int hashCode() {
201        return Objects.hash(id);
202    }
203
204    @Override
205    public boolean equals(Object obj) {
206        if (this == obj)
207            return true;
208        if (obj == null || getClass() != obj.getClass())
209            return false;
210        Note note = (Note) obj;
211        return id == note.id;
212    }
213
214    @Override
215    public String toString() {
216        return tr("Note") + ' ' + id + ": " + getFirstComment();
217    }
218}