001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions.upload; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.IOException; 007import java.util.HashMap; 008import java.util.Map; 009import java.util.stream.Collectors; 010 011import javax.swing.JOptionPane; 012 013import org.openstreetmap.josm.data.notes.Note; 014import org.openstreetmap.josm.data.notes.NoteComment; 015import org.openstreetmap.josm.data.osm.NoteData; 016import org.openstreetmap.josm.gui.ExceptionDialogUtil; 017import org.openstreetmap.josm.gui.MainApplication; 018import org.openstreetmap.josm.gui.PleaseWaitRunnable; 019import org.openstreetmap.josm.gui.progress.ProgressMonitor; 020import org.openstreetmap.josm.io.OsmApi; 021import org.openstreetmap.josm.io.OsmTransferCanceledException; 022import org.openstreetmap.josm.io.OsmTransferException; 023import org.openstreetmap.josm.tools.Logging; 024import org.xml.sax.SAXException; 025 026/** 027 * Class for uploading note changes to the server 028 */ 029public class UploadNotesTask { 030 031 private NoteData noteData; 032 033 /** 034 * Upload notes with modifications to the server 035 * @param noteData Note dataset with changes to upload 036 * @param progressMonitor progress monitor for user feedback 037 */ 038 public void uploadNotes(NoteData noteData, ProgressMonitor progressMonitor) { 039 this.noteData = noteData; 040 MainApplication.worker.submit(new UploadTask(tr("Uploading modified notes"), progressMonitor)); 041 } 042 043 private class UploadTask extends PleaseWaitRunnable { 044 045 private boolean isCanceled; 046 private final Map<Note, Note> updatedNotes = new HashMap<>(); 047 private final Map<Note, Exception> failedNotes = new HashMap<>(); 048 049 /** 050 * Constructs a new {@code UploadTask}. 051 * @param title message for the user 052 * @param monitor progress monitor 053 */ 054 UploadTask(String title, ProgressMonitor monitor) { 055 super(title, monitor, false); 056 } 057 058 @Override 059 protected void cancel() { 060 Logging.debug("note upload canceled"); 061 isCanceled = true; 062 } 063 064 @Override 065 protected void realRun() throws SAXException, IOException, OsmTransferException { 066 ProgressMonitor monitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 067 OsmApi api = OsmApi.getOsmApi(); 068 for (Note note : noteData.getNotes()) { 069 if (isCanceled) { 070 Logging.info("Note upload interrupted by user"); 071 break; 072 } 073 for (NoteComment comment : note.getComments()) { 074 if (comment.isNew()) { 075 Logging.debug("found note change to upload"); 076 processNoteComment(monitor, api, note, comment); 077 } 078 } 079 } 080 } 081 082 private void processNoteComment(ProgressMonitor monitor, OsmApi api, Note note, NoteComment comment) { 083 try { 084 if (updatedNotes.containsKey(note)) { 085 // if note has been created earlier in this task, obtain its real id and not use the placeholder id 086 note = updatedNotes.get(note); 087 } 088 Note newNote; 089 switch (comment.getNoteAction()) { 090 case OPENED: 091 Logging.debug("opening new note"); 092 newNote = api.createNote(note.getLatLon(), comment.getText(), monitor); 093 break; 094 case CLOSED: 095 Logging.debug("closing note {0}", note.getId()); 096 newNote = api.closeNote(note, comment.getText(), monitor); 097 break; 098 case COMMENTED: 099 Logging.debug("adding comment to note {0}", note.getId()); 100 newNote = api.addCommentToNote(note, comment.getText(), monitor); 101 break; 102 case REOPENED: 103 Logging.debug("reopening note {0}", note.getId()); 104 newNote = api.reopenNote(note, comment.getText(), monitor); 105 break; 106 default: 107 newNote = null; 108 } 109 updatedNotes.put(note, newNote); 110 } catch (OsmTransferException e) { 111 Logging.error("Failed to upload note to server: {0}", note.getId()); 112 Logging.error(e); 113 if (!(e instanceof OsmTransferCanceledException)) { 114 failedNotes.put(note, e); 115 } 116 } 117 } 118 119 /** Updates the note layer with uploaded notes and notifies the user of any upload failures */ 120 @Override 121 protected void finish() { 122 if (Logging.isDebugEnabled()) { 123 Logging.debug("finish called in notes upload task. Notes to update: {0}", updatedNotes.size()); 124 } 125 noteData.updateNotes(updatedNotes); 126 if (!failedNotes.isEmpty()) { 127 String message = failedNotes.entrySet().stream() 128 .map(entry -> tr("Note {0} failed: {1}", entry.getKey().getId(), entry.getValue().getMessage())) 129 .collect(Collectors.joining("\n")); 130 Logging.error("Notes failed to upload: " + message); 131 JOptionPane.showMessageDialog(MainApplication.getMap(), message, 132 tr("Notes failed to upload"), JOptionPane.ERROR_MESSAGE); 133 ExceptionDialogUtil.explainException(failedNotes.values().iterator().next()); 134 } 135 } 136 } 137}