source: josm/trunk/src/org/openstreetmap/josm/data/osm/NoteData.java@ 12673

Last change on this file since 12673 was 12630, checked in by Don-vip, 7 years ago

see #15182 - deprecate Main.map and Main.isDisplayingMapView(). Replacements: gui.MainApplication.getMap() / gui.MainApplication.isDisplayingMapView()

  • Property svn:eol-style set to native
File size: 10.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.Comparator;
8import java.util.Date;
9import java.util.List;
10import java.util.Map;
11
12import org.openstreetmap.josm.data.coor.LatLon;
13import org.openstreetmap.josm.data.notes.Note;
14import org.openstreetmap.josm.data.notes.Note.State;
15import org.openstreetmap.josm.data.notes.NoteComment;
16import org.openstreetmap.josm.gui.JosmUserIdentityManager;
17import org.openstreetmap.josm.gui.MainApplication;
18import org.openstreetmap.josm.gui.MapFrame;
19import org.openstreetmap.josm.tools.ListenerList;
20import org.openstreetmap.josm.tools.Logging;
21
22/**
23 * Class to hold and perform operations on a set of notes
24 */
25public class NoteData {
26
27 /**
28 * A listener that can be informed on note data changes.
29 * @author Michael Zangl
30 * @since 12343
31 */
32 public interface NoteDataUpdateListener {
33 /**
34 * Called when the note data is updated
35 * @param data The data that was changed
36 */
37 void noteDataUpdated(NoteData data);
38
39 /**
40 * The selected node was changed
41 * @param noteData The data of which the selected node was changed
42 */
43 void selectedNoteChanged(NoteData noteData);
44 }
45
46 private long newNoteId = -1;
47
48 private final Storage<Note> noteList;
49 private Note selectedNote;
50 private Comparator<Note> comparator = Note.DEFAULT_COMPARATOR;
51
52 private final ListenerList<NoteDataUpdateListener> listeners = ListenerList.create();
53
54 /**
55 * Construct a new note container with a given list of notes
56 * @param notes The list of notes to populate the container with
57 */
58 public NoteData(Collection<Note> notes) {
59 noteList = new Storage<>();
60 if (notes != null) {
61 for (Note note : notes) {
62 noteList.add(note);
63 if (note.getId() <= newNoteId) {
64 newNoteId = note.getId() - 1;
65 }
66 }
67 }
68 }
69
70 /**
71 * Returns the notes stored in this layer
72 * @return collection of notes
73 */
74 public Collection<Note> getNotes() {
75 return Collections.unmodifiableCollection(noteList);
76 }
77
78 /**
79 * Returns the notes stored in this layer sorted according to {@link #comparator}
80 * @return sorted collection of notes
81 */
82 public Collection<Note> getSortedNotes() {
83 final List<Note> list = new ArrayList<>(noteList);
84 list.sort(comparator);
85 return list;
86 }
87
88 /**
89 * Returns the currently selected note
90 * @return currently selected note
91 */
92 public Note getSelectedNote() {
93 return selectedNote;
94 }
95
96 /**
97 * Set a selected note. Causes the dialog to select the note and
98 * the note layer to draw the selected note's comments.
99 * @param note Selected note. Null indicates no selection
100 */
101 public void setSelectedNote(Note note) {
102 selectedNote = note;
103 MapFrame map = MainApplication.getMap();
104 if (map != null) {
105 map.noteDialog.selectionChanged();
106 }
107 listeners.fireEvent(l -> l.selectedNoteChanged(this));
108 }
109
110 /**
111 * Return whether or not there are any changes in the note data set.
112 * These changes may need to be either uploaded or saved.
113 * @return true if local modifications have been made to the note data set. False otherwise.
114 */
115 public synchronized boolean isModified() {
116 for (Note note : noteList) {
117 if (note.getId() < 0) { //notes with negative IDs are new
118 return true;
119 }
120 for (NoteComment comment : note.getComments()) {
121 if (comment.isNew()) {
122 return true;
123 }
124 }
125 }
126 return false;
127 }
128
129 /**
130 * Add notes to the data set. It only adds a note if the ID is not already present
131 * @param newNotes A list of notes to add
132 */
133 public synchronized void addNotes(Collection<Note> newNotes) {
134 for (Note newNote : newNotes) {
135 if (!noteList.contains(newNote)) {
136 noteList.add(newNote);
137 } else {
138 final Note existingNote = noteList.get(newNote);
139 final boolean isDirty = existingNote.getComments().stream().anyMatch(NoteComment::isNew);
140 if (!isDirty) {
141 noteList.put(newNote);
142 } else {
143 // TODO merge comments?
144 Logging.info("Keeping existing note id={0} with uncommitted changes", String.valueOf(newNote.getId()));
145 }
146 }
147 if (newNote.getId() <= newNoteId) {
148 newNoteId = newNote.getId() - 1;
149 }
150 }
151 dataUpdated();
152 }
153
154 /**
155 * Create a new note
156 * @param location Location of note
157 * @param text Required comment with which to open the note
158 */
159 public synchronized void createNote(LatLon location, String text) {
160 if (text == null || text.isEmpty()) {
161 throw new IllegalArgumentException("Comment can not be blank when creating a note");
162 }
163 Note note = new Note(location);
164 note.setCreatedAt(new Date());
165 note.setState(State.OPEN);
166 note.setId(newNoteId--);
167 NoteComment comment = new NoteComment(new Date(), getCurrentUser(), text, NoteComment.Action.OPENED, true);
168 note.addComment(comment);
169 if (Logging.isDebugEnabled()) {
170 Logging.debug("Created note {0} with comment: {1}", note.getId(), text);
171 }
172 noteList.add(note);
173 dataUpdated();
174 }
175
176 /**
177 * Add a new comment to an existing note
178 * @param note Note to add comment to. Must already exist in the layer
179 * @param text Comment to add
180 */
181 public synchronized void addCommentToNote(Note note, String text) {
182 if (!noteList.contains(note)) {
183 throw new IllegalArgumentException("Note to modify must be in layer");
184 }
185 if (note.getState() == State.CLOSED) {
186 throw new IllegalStateException("Cannot add a comment to a closed note");
187 }
188 if (Logging.isDebugEnabled()) {
189 Logging.debug("Adding comment to note {0}: {1}", note.getId(), text);
190 }
191 NoteComment comment = new NoteComment(new Date(), getCurrentUser(), text, NoteComment.Action.COMMENTED, true);
192 note.addComment(comment);
193 dataUpdated();
194 }
195
196 /**
197 * Close note with comment
198 * @param note Note to close. Must already exist in the layer
199 * @param text Comment to attach to close action, if desired
200 */
201 public synchronized void closeNote(Note note, String text) {
202 if (!noteList.contains(note)) {
203 throw new IllegalArgumentException("Note to close must be in layer");
204 }
205 if (note.getState() != State.OPEN) {
206 throw new IllegalStateException("Cannot close a note that isn't open");
207 }
208 if (Logging.isDebugEnabled()) {
209 Logging.debug("closing note {0} with comment: {1}", note.getId(), text);
210 }
211 NoteComment comment = new NoteComment(new Date(), getCurrentUser(), text, NoteComment.Action.CLOSED, true);
212 note.addComment(comment);
213 note.setState(State.CLOSED);
214 note.setClosedAt(new Date());
215 dataUpdated();
216 }
217
218 /**
219 * Reopen a closed note.
220 * @param note Note to reopen. Must already exist in the layer
221 * @param text Comment to attach to the reopen action, if desired
222 */
223 public synchronized void reOpenNote(Note note, String text) {
224 if (!noteList.contains(note)) {
225 throw new IllegalArgumentException("Note to reopen must be in layer");
226 }
227 if (note.getState() != State.CLOSED) {
228 throw new IllegalStateException("Cannot reopen a note that isn't closed");
229 }
230 Logging.debug("reopening note {0} with comment: {1}", note.getId(), text);
231 NoteComment comment = new NoteComment(new Date(), getCurrentUser(), text, NoteComment.Action.REOPENED, true);
232 note.addComment(comment);
233 note.setState(State.OPEN);
234 dataUpdated();
235 }
236
237 private void dataUpdated() {
238 if (MainApplication.isDisplayingMapView()) {
239 MainApplication.getMap().noteDialog.setNotes(getSortedNotes());
240 }
241 listeners.fireEvent(l -> l.noteDataUpdated(this));
242 }
243
244 private static User getCurrentUser() {
245 JosmUserIdentityManager userMgr = JosmUserIdentityManager.getInstance();
246 return User.createOsmUser(userMgr.getUserId(), userMgr.getUserName());
247 }
248
249 /**
250 * Updates notes with new state. Primarily to be used when updating the
251 * note layer after uploading note changes to the server.
252 * @param updatedNotes Map containing the original note as the key and the updated note as the value
253 */
254 public synchronized void updateNotes(Map<Note, Note> updatedNotes) {
255 for (Map.Entry<Note, Note> entry : updatedNotes.entrySet()) {
256 Note oldNote = entry.getKey();
257 Note newNote = entry.getValue();
258 boolean reindex = oldNote.hashCode() != newNote.hashCode();
259 if (reindex) {
260 noteList.removeElem(oldNote);
261 }
262 oldNote.updateWith(newNote);
263 if (reindex) {
264 noteList.add(oldNote);
265 }
266 }
267 dataUpdated();
268 }
269
270 /**
271 * Returns the current comparator being used to sort the note list.
272 * @return The current comparator being used to sort the note list
273 */
274 public Comparator<Note> getCurrentSortMethod() {
275 return comparator;
276 }
277
278 /** Set the comparator to be used to sort the note list. Several are available
279 * as public static members of this class.
280 * @param comparator - The Note comparator to sort by
281 */
282 public void setSortMethod(Comparator<Note> comparator) {
283 this.comparator = comparator;
284 dataUpdated();
285 }
286
287 /**
288 * Adds a listener that listens to node data changes
289 * @param listener The listener
290 */
291 public void addNoteDataUpdateListener(NoteDataUpdateListener listener) {
292 listeners.addListener(listener);
293 }
294
295 /**
296 * Removes a listener that listens to node data changes
297 * @param listener The listener
298 */
299 public void removeNoteDataUpdateListener(NoteDataUpdateListener listener) {
300 listeners.removeListener(listener);
301 }
302}
Note: See TracBrowser for help on using the repository browser.