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