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;
009
010import javax.swing.JOptionPane;
011
012import org.openstreetmap.josm.Main;
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.PleaseWaitRunnable;
017import org.openstreetmap.josm.gui.progress.ProgressMonitor;
018import org.openstreetmap.josm.io.OsmApi;
019import org.openstreetmap.josm.io.OsmTransferException;
020import org.xml.sax.SAXException;
021
022/**
023 * Class for uploading note changes to the server
024 */
025public class UploadNotesTask {
026
027    private UploadTask uploadTask;
028    private NoteData noteData;
029
030    /**
031     * Upload notes with modifications to the server
032     * @param noteData Note dataset with changes to upload
033     * @param progressMonitor progress monitor for user feedback
034     */
035    public void uploadNotes(NoteData noteData, ProgressMonitor progressMonitor) {
036        this.noteData = noteData;
037        uploadTask = new UploadTask(tr("Uploading modified notes"), progressMonitor);
038        Main.worker.submit(uploadTask);
039    }
040
041    private class UploadTask extends PleaseWaitRunnable {
042
043        private boolean isCanceled = false;
044        Map<Note, Note> updatedNotes = new HashMap<>();
045        Map<Note, Exception> failedNotes = new HashMap<>();
046
047        /**
048         * Constructs a new {@code UploadTask}.
049         * @param title message for the user
050         * @param monitor progress monitor
051         */
052        public UploadTask(String title, ProgressMonitor monitor) {
053            super(title, monitor, false);
054        }
055
056        @Override
057        protected void cancel() {
058            if (Main.isDebugEnabled()) {
059                Main.debug("note upload canceled");
060            }
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                    Main.info("Note upload interrupted by user");
071                    break;
072                }
073                for (NoteComment comment : note.getComments()) {
074                    if (comment.getIsNew()) {
075                        if (Main.isDebugEnabled()) {
076                            Main.debug("found note change to upload");
077                        }
078                        processNoteComment(monitor, api, note, comment);
079                    }
080                }
081            }
082        }
083
084        private void processNoteComment(ProgressMonitor monitor, OsmApi api, Note note, NoteComment comment) {
085            try {
086                Note newNote;
087                switch (comment.getNoteAction()) {
088                case opened:
089                    if (Main.isDebugEnabled()) {
090                        Main.debug("opening new note");
091                    }
092                    newNote = api.createNote(note.getLatLon(), comment.getText(), monitor);
093                    note.setId(newNote.getId());
094                    break;
095                case closed:
096                    if (Main.isDebugEnabled()) {
097                        Main.debug("closing note " + note.getId());
098                    }
099                    newNote = api.closeNote(note, comment.getText(), monitor);
100                    break;
101                case commented:
102                    if (Main.isDebugEnabled()) {
103                        Main.debug("adding comment to note " + note.getId());
104                    }
105                    newNote = api.addCommentToNote(note, comment.getText(), monitor);
106                    break;
107                case reopened:
108                    if (Main.isDebugEnabled()) {
109                        Main.debug("reopening note " + note.getId());
110                    }
111                    newNote = api.reopenNote(note, comment.getText(), monitor);
112                    break;
113                default:
114                    newNote = null;
115                }
116                updatedNotes.put(note, newNote);
117            } catch (Exception e) {
118                Main.error("Failed to upload note to server: " + note.getId());
119                failedNotes.put(note, e);
120            }
121        }
122
123        /** Updates the note layer with uploaded notes and notifies the user of any upload failures */
124        @Override
125        protected void finish() {
126            if (Main.isDebugEnabled()) {
127                Main.debug("finish called in notes upload task. Notes to update: " + updatedNotes.size());
128            }
129            noteData.updateNotes(updatedNotes);
130            if (!failedNotes.isEmpty()) {
131                Main.error("Some notes failed to upload");
132                StringBuilder sb = new StringBuilder();
133                for (Map.Entry<Note, Exception> entry : failedNotes.entrySet()) {
134                    sb.append(tr("Note {0} failed: {1}", entry.getKey().getId(), entry.getValue().getMessage()));
135                    sb.append("\n");
136                }
137                Main.error("Notes failed to upload: " + sb.toString());
138                JOptionPane.showMessageDialog(Main.map, sb.toString(), tr("Notes failed to upload"), JOptionPane.ERROR_MESSAGE);
139            }
140        }
141
142    }
143
144}