Changeset 2303 in josm for trunk


Ignore:
Timestamp:
2009-10-24T10:20:45+02:00 (15 years ago)
Author:
Gubaer
Message:

see #3761: Precondition failed: Node is still used by way
Also added help for this use case, see help

Location:
trunk/src/org/openstreetmap/josm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/DownloadReferrersAction.java

    r2271 r2303  
    88import java.io.IOException;
    99import java.util.Collection;
     10import java.util.HashMap;
     11import java.util.Map;
     12import java.util.Map.Entry;
    1013
    1114import javax.swing.JOptionPane;
     
    1518import org.openstreetmap.josm.data.osm.DataSet;
    1619import org.openstreetmap.josm.data.osm.OsmPrimitive;
     20import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    1721import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
    18 import org.openstreetmap.josm.gui.DefaultNameFormatter;
    1922import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    2023import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2124import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    22 import org.openstreetmap.josm.io.OsmApi;
    2325import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
    2426import org.openstreetmap.josm.io.OsmTransferException;
     27import org.openstreetmap.josm.tools.ExceptionUtil;
    2528import org.openstreetmap.josm.tools.Shortcut;
    2629import org.xml.sax.SAXException;
     
    3033 * server.
    3134 *
    32  *
    3335 */
    3436public class DownloadReferrersAction extends JosmAction{
     
    4042
    4143    /**
    42      * Downloads the primitives referring to the primitives in <code>primitives</code>.
     44     * Downloads the primitives referring to the primitives in <code>primitives</code>
     45     * into the target layer <code>targetLayer</code>.
    4346     * Does nothing if primitives is null or empty.
    4447     *
    45      * @param primitives the collection of primitives.
    46      */
    47     public void downloadReferrers(Collection<OsmPrimitive> primitives) {
    48         if (primitives == null || primitives.isEmpty()) return;
    49         Main.worker.submit(new DownloadReferrersTask(primitives));
    50     }
    51 
     48     * @param targetLayer  the target layer. Must not be null.
     49     * @param children the collection of child primitives.
     50     * @exception IllegalArgumentException thrown if targetLayer is null
     51     */
     52    static public void downloadReferrers(OsmDataLayer targetLayer, Collection<OsmPrimitive> children) throws IllegalArgumentException {
     53        if (children == null || children.isEmpty()) return;
     54        Main.worker.submit(new DownloadReferrersTask(targetLayer, children));
     55    }
     56
     57    /**
     58     * Downloads the primitives referring to the primitives in <code>primitives</code>
     59     * into the target layer <code>targetLayer</code>.
     60     * Does nothing if primitives is null or empty.
     61     *
     62     * @param targetLayer  the target layer. Must not be null.
     63     * @param children the collection of primitives, given as map of ids and types
     64     * @exception IllegalArgumentException thrown if targetLayer is null
     65     */
     66    static public void downloadReferrers(OsmDataLayer targetLayer, Map<Long, OsmPrimitiveType> children) throws IllegalArgumentException {
     67        if (children == null || children.isEmpty()) return;
     68        Main.worker.submit(new DownloadReferrersTask(targetLayer, children));
     69    }
     70
     71    /**
     72     * Downloads the primitives referring to the primitive given by <code>id</code> and
     73     * <code>type</code>.
     74     *
     75     *
     76     * @param targetLayer  the target layer. Must not be null.
     77     * @param id the primitive id. id > 0 required.
     78     * @param type the primitive type. type != null required
     79     * @exception IllegalArgumentException thrown if targetLayer is null
     80     * @exception IllegalArgumentException thrown if id <= 0
     81     * @exception IllegalArgumentException thrown if type == null
     82     */
     83    static public void downloadReferrers(OsmDataLayer targetLayer, long id, OsmPrimitiveType type) throws IllegalArgumentException {
     84        if (id <= 0)
     85            throw new IllegalArgumentException(tr("Id > 0 required, got {0}", id));
     86        if (type == null)
     87            throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
     88        Main.worker.submit(new DownloadReferrersTask(targetLayer, id, type));
     89    }
    5290
    5391    public void actionPerformed(ActionEvent e) {
     
    5896            return;
    5997        Collection<OsmPrimitive> primitives = layer.data.getSelected();
    60         downloadReferrers(primitives);
     98        downloadReferrers(layer,primitives);
    6199    }
    62100
     
    65103     *
    66104     */
    67     class DownloadReferrersTask extends PleaseWaitRunnable {
    68         private DataSet ds;
     105    public static class DownloadReferrersTask extends PleaseWaitRunnable {
    69106        private boolean cancelled;
    70         Exception lastException;
    71         private Collection<OsmPrimitive> primitives;
     107        private Exception lastException;
     108        private OsmServerBackreferenceReader reader;
     109        /** the target layer */
     110        private OsmDataLayer targetLayer;
     111        /** the collection of child primitives */
     112        private Map<Long, OsmPrimitiveType> children;
     113        /** the parents */
    72114        private DataSet parents;
    73115
    74         public DownloadReferrersTask(Collection<OsmPrimitive> primitives) {
     116        /**
     117         * constructor
     118         *
     119         * @param targetLayer  the target layer for the downloaded primitives. Must not be null.
     120         * @param children the collection of child primitives for which parents are to be downloaded
     121         *
     122         */
     123        public DownloadReferrersTask(OsmDataLayer targetLayer, Collection<OsmPrimitive> children) {
    75124            super("Download referrers", false /* don't ignore exception*/);
     125            if (targetLayer == null)
     126                throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "targetLayer"));
    76127            cancelled = false;
    77             this.primitives = primitives;
     128            this.children = new HashMap<Long, OsmPrimitiveType>();
     129            if (children != null) {
     130                for (OsmPrimitive p: children) {
     131                    if (! p.isNew()) {
     132                        this.children.put(p.getId(), OsmPrimitiveType.from(p));
     133                    }
     134                }
     135            }
     136            this.targetLayer = targetLayer;
    78137            parents = new DataSet();
    79138        }
    80139
    81         protected void showLastException() {
    82             String msg = lastException.getMessage();
    83             if (msg == null) {
    84                 msg = lastException.toString();
    85             }
    86             JOptionPane.showMessageDialog(
    87                     Main.map,
    88                     msg,
    89                     tr("Error"),
    90                     JOptionPane.ERROR_MESSAGE
    91             );
     140        /**
     141         * constructor
     142         *
     143         * @param targetLayer  the target layer for the downloaded primitives. Must not be null.
     144         * @param primitives  the collection of children for which parents are to be downloaded. Children
     145         * are specified by their id and  their type.
     146         *
     147         */
     148        public DownloadReferrersTask(OsmDataLayer targetLayer, Map<Long, OsmPrimitiveType> children) {
     149            super("Download referrers", false /* don't ignore exception*/);
     150            if (targetLayer == null)
     151                throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "targetLayer"));
     152            cancelled = false;
     153            this.children = new HashMap<Long, OsmPrimitiveType>();
     154            if (children != null) {
     155                for (Entry<Long, OsmPrimitiveType> entry : children.entrySet()) {
     156                    if (entry.getKey() > 0 && entry.getValue() != null) {
     157                        children.put(entry.getKey(), entry.getValue());
     158                    }
     159                }
     160            }
     161            this.targetLayer = targetLayer;
     162            parents = new DataSet();
     163        }
     164
     165        /**
     166         * constructor
     167         *
     168         * @param targetLayer  the target layer. Must not be null.
     169         * @param id the primitive id. id > 0 required.
     170         * @param type the primitive type. type != null required
     171         * @exception IllegalArgumentException thrown if id <= 0
     172         * @exception IllegalArgumentException thrown if type == null
     173         * @exception IllegalArgumentException thrown if targetLayer == null
     174         *
     175         */
     176        public DownloadReferrersTask(OsmDataLayer targetLayer, long id, OsmPrimitiveType type) throws IllegalArgumentException {
     177            super("Download referrers", false /* don't ignore exception*/);
     178            if (targetLayer == null)
     179                throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "targetLayer"));
     180            if (id <= 0)
     181                throw new IllegalArgumentException(tr("Id > 0 required, got {0}", id));
     182            if (type == null)
     183                throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "type"));
     184            cancelled = false;
     185            this.children = new HashMap<Long, OsmPrimitiveType>();
     186            this.children.put(id, type);
     187            this.targetLayer = targetLayer;
     188            parents = new DataSet();
    92189        }
    93190
     
    95192        protected void cancel() {
    96193            cancelled = true;
    97             OsmApi.getOsmApi().cancel();
     194            synchronized(this) {
     195                if (reader != null) {
     196                    reader.cancel();
     197                }
     198            }
    98199        }
    99200
     
    103204                return;
    104205            if (lastException != null) {
    105                 showLastException();
     206                ExceptionUtil.explainException(lastException);
    106207                return;
    107208            }
    108209
    109             MergeVisitor visitor = new MergeVisitor(Main.map.mapView.getEditLayer().data, parents);
     210            MergeVisitor visitor = new MergeVisitor(targetLayer.data, parents);
    110211            visitor.merge();
    111212            SwingUtilities.invokeLater(
    112213                    new Runnable() {
    113214                        public void run() {
    114                             Main.map.mapView.getEditLayer().fireDataChange();
     215                            targetLayer.fireDataChange();
    115216                            Main.map.mapView.repaint();
    116217                        }
     
    119220            if (visitor.getConflicts().isEmpty())
    120221                return;
    121             Main.map.mapView.getEditLayer().getConflicts().add(visitor.getConflicts());
     222            targetLayer.getConflicts().add(visitor.getConflicts());
    122223            JOptionPane.showMessageDialog(
    123224                    Main.parent,
     
    130231        }
    131232
    132         protected void downloadParents(OsmPrimitive primitive, ProgressMonitor progressMonitor) throws OsmTransferException{
    133             OsmServerBackreferenceReader reader = new OsmServerBackreferenceReader(primitive);
     233        protected void downloadParents(long id, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException{
     234            reader = new OsmServerBackreferenceReader(id, type);
    134235            DataSet ds = reader.parseOsm(progressMonitor);
     236            synchronized(this) { // avoid race condition in cancel()
     237                reader = null;
     238            }
    135239            MergeVisitor visitor = new MergeVisitor(parents, ds);
    136240            visitor.merge();
     
    140244        protected void realRun() throws SAXException, IOException, OsmTransferException {
    141245            try {
    142                 progressMonitor.setTicksCount(primitives.size());
     246                progressMonitor.setTicksCount(children.size());
    143247                int i=1;
    144                 for (OsmPrimitive primitive: primitives) {
     248                for (Entry<Long, OsmPrimitiveType> entry: children.entrySet()) {
    145249                    if (cancelled)
    146250                        return;
    147                     progressMonitor.subTask(tr("({0}/{1}) Loading parents of primitive {2}", i+1,primitives.size(), primitive.getDisplayName(DefaultNameFormatter.getInstance())));
    148                     downloadParents(primitive, progressMonitor.createSubTaskMonitor(1, false));
     251                    String msg = "";
     252                    switch(entry.getValue()) {
     253                        case NODE: msg = tr("({0}/{1}) Loading parents of node {2}", i+1,children.size(), entry.getKey()); break;
     254                        case WAY: msg = tr("({0}/{1}) Loading parents of way {2}", i+1,children.size(), entry.getKey()); break;
     255                        case RELATION: msg = tr("({0}/{1}) Loading parents of relation {2}", i+1,children.size(), entry.getKey()); break;
     256                    }
     257                    progressMonitor.subTask(msg);
     258                    downloadParents(entry.getKey(), entry.getValue(), progressMonitor.createSubTaskMonitor(1, false));
    149259                    i++;
    150260                }
  • trunk/src/org/openstreetmap/josm/actions/UploadAction.java

    r2273 r2303  
    228228        String lbl = "";
    229229        switch(primitiveType) {
    230         case NODE: lbl =  tr("Synchronize node {0} only", id); break;
    231         case WAY: lbl =  tr("Synchronize way {0} only", id); break;
    232         case RELATION: lbl =  tr("Synchronize relation {0} only", id); break;
     230            case NODE: lbl =  tr("Synchronize node {0} only", id); break;
     231            case WAY: lbl =  tr("Synchronize way {0} only", id); break;
     232            case RELATION: lbl =  tr("Synchronize relation {0} only", id); break;
    233233        }
    234234        ButtonSpec[] spec = new ButtonSpec[] {
     
    271271                spec,
    272272                spec[0],
    273                 "Concepts/Conflict"
     273                "/Concepts/Conflict"
    274274        );
    275275        switch(ret) {
    276         case 0: synchronizePrimitive(primitiveType, id); break;
    277         case 1: synchronizeDataSet(); break;
    278         default: return;
     276            case 0: synchronizePrimitive(primitiveType, id); break;
     277            case 1: synchronizeDataSet(); break;
     278            default: return;
    279279        }
    280280    }
     
    341341    }
    342342
     343
     344    /**
     345     * Handles the case where deleting a node failed because it is still in use in
     346     * a non-deleted way on the server.
     347     */
     348    protected void handleUploadConflictForNodeStillInUse(long nodeId, long wayId) {
     349        ButtonSpec[] options = new ButtonSpec[] {
     350                new ButtonSpec(
     351                        tr("Prepare conflict resolution"),
     352                        ImageProvider.get("ok"),
     353                        tr("Click to download all parent ways for node {0}", nodeId),
     354                        null /* no specific help context */
     355                ),
     356                new ButtonSpec(
     357                        tr("Cancel"),
     358                        ImageProvider.get("cancel"),
     359                        tr("Click to cancel and to resume editing the map", nodeId),
     360                        null /* no specific help context */
     361                )
     362        };
     363        String msg =  tr("<html>Uploading <strong>failed</strong> because you tried "
     364                + "to delete node {0} which is still in use in way {1}.<br><br>"
     365                + "Click <strong>{2}</strong> to download all parent ways of node {0}.<br>"
     366                + "If necessary JOSM will create conflicts which you can resolve in the Conflict Resolution Dialog."
     367                + "</html>",
     368                nodeId, wayId, options[0].text
     369        );
     370
     371        int ret = HelpAwareOptionPane.showOptionDialog(
     372                Main.parent,
     373                msg,
     374                tr("Node still in use"),
     375                JOptionPane.ERROR_MESSAGE,
     376                null,
     377                options,
     378                options[0],
     379                "/Action/Upload#NodeStillInUseInWay"
     380        );
     381        if (ret != 0) return;
     382        DownloadReferrersAction.downloadReferrers(Main.map.mapView.getEditLayer(), nodeId, OsmPrimitiveType.NODE);
     383    }
     384
    343385    /**
    344386     * handles an upload conflict, i.e. an error indicated by a HTTP return code 409.
     
    361403            return;
    362404        }
    363         logger.warning(tr("Warning: error header \"{0}\" did not match expected pattern \"{1}\"", e.getErrorHeader(),pattern));
     405        pattern = "Node (\\d+) is still used by way (\\d+).";
     406        p = Pattern.compile(pattern);
     407        m = p.matcher(e.getErrorHeader());
     408        if (m.matches()) {
     409            handleUploadConflictForNodeStillInUse(Long.parseLong(m.group(1)), Long.parseLong(m.group(2)));
     410            return;
     411        }
     412        logger.warning(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
    364413        handleUploadConflictForUnknownConflict();
     414    }
     415
     416    /**
     417     * handles an precondition failed conflict, i.e. an error indicated by a HTTP return code 412.
     418     *
     419     * @param e  the exception
     420     */
     421    protected void handlePreconditionFailed(OsmApiException e) {
     422        String pattern = "Precondition failed: Node (\\d+) is still used by way (\\d+).";
     423        Pattern p = Pattern.compile(pattern);
     424        Matcher m = p.matcher(e.getErrorHeader());
     425        if (m.matches()) {
     426            handleUploadConflictForNodeStillInUse(Long.parseLong(m.group(1)), Long.parseLong(m.group(2)));
     427            return;
     428        }
     429        logger.warning(tr("Warning: error header \"{0}\" did not match with an expected pattern", e.getErrorHeader()));
     430        ExceptionDialogUtil.explainPreconditionFailed(e);
    365431    }
    366432
     
    433499            //
    434500            else if (ex.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED) {
    435                 ExceptionDialogUtil.explainPreconditionFailed(ex);
     501                handlePreconditionFailed(ex);
    436502                return;
    437503            }
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java

    r2291 r2303  
    2323 * into it.
    2424 *
    25  * @author imi
    26  * @author Gubaer
    2725 */
    2826public class MergeVisitor extends AbstractVisitor {
     
    188186                if (!mergedNode.isDeleted()) {
    189187                    newNodes.add(mergedNode);
     188                } else {
     189                    // we've removed a node from a way during merging.
     190                    // Flag the way as being modified.
     191                    //
     192                    w.setModified(true);
    190193                }
    191194                replacedSomething =  true;
  • trunk/src/org/openstreetmap/josm/gui/HelpAwareOptionPane.java

    r2301 r2303  
    1515import javax.swing.JButton;
    1616import javax.swing.JDialog;
     17import javax.swing.JLabel;
    1718import javax.swing.JOptionPane;
    1819
     
    148149            }
    149150        }
     151
     152        if (msg instanceof String) {
     153            msg = new JLabel((String)msg);
     154        }
     155
    150156        final JOptionPane pane = new JOptionPane(
    151157                msg,
  • trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java

    r2273 r2303  
    1414import org.openstreetmap.josm.data.osm.Way;
    1515import org.openstreetmap.josm.data.osm.visitor.MergeVisitor;
     16import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    1617import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    1718
     
    134135        progressMonitor.beginTask(null, 2);
    135136        try {
    136             progressMonitor.indeterminateSubTask(tr("Contacting OSM Server..."));
     137            progressMonitor.indeterminateSubTask(tr("Downloading from OSM Server..."));
    137138            StringBuffer sb = new StringBuffer();
    138139            sb.append(primitiveType.getAPIName())
     
    160161        }
    161162    }
    162     /**
    163 
     163
     164    /**
    164165     * Reads referring relations from the API server and replies them in a {@see DataSet}
    165166     *
     167     * @param progressMonitor the progress monitor
    166168     * @return the data set
    167169     * @throws OsmTransferException
     
    213215     *
    214216     * @param ds the original dataset
     217     * @param progressMonitor  the progress monitor
    215218     * @return the modified dataset
    216219     * @throws OsmTransferException thrown if an exception occurs.
     
    251254     * replies them as {@see DataSet}
    252255     *
     256     * @param progressMonitor the progress monitor. Set to {@see NullProgressMonitor#INSTANCE} if null.
    253257     * @return the dataset with the referring primitives
    254258     * @exception OsmTransferException thrown if an error occurs while communicating with the server
     
    256260    @Override
    257261    public DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException {
    258         progressMonitor.beginTask(null, 3);
    259         try {
     262        if (progressMonitor == null) {
     263            progressMonitor = NullProgressMonitor.INSTANCE;
     264        }
     265        try {
     266            progressMonitor.beginTask(null, 3);
    260267            DataSet ret = new DataSet();
    261268            if (primitiveType.equals(OsmPrimitiveType.NODE)) {
Note: See TracChangeset for help on using the changeset viewer.