Ignore:
Timestamp:
23.06.2009 22:03:37 (3 years ago)
Author:
Gubaer
Message:

new: MultiFetchServerObjectReader using APIs Multi Fetch method
update: now uses Multi Fetch to check for deleted primitives on the server
update: now uses Multi Fetch to update the selected primitives with the state from the server
fixed: cleaned up merging in MergeVisitor
new: conflict resolution dialog; now resolves conflicts due to different visibilities
new: replacement for realEqual() on OsmPrimitive and derived classes; realEqual now @deprecated
fixed: cleaning up OsmReader
fixed: progress indication in OsmApi

File:
1 edited

Legend:

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

    r1670 r1690  
    66import java.awt.event.ActionEvent; 
    77import java.awt.event.KeyEvent; 
    8 import java.io.IOException; 
    9 import java.net.HttpURLConnection; 
    10 import java.util.ArrayList; 
    118import java.util.Collection; 
     9import java.util.HashSet; 
     10import java.util.Set; 
    1211 
    1312import javax.swing.JOptionPane; 
     
    1514import org.openstreetmap.josm.Main; 
    1615import org.openstreetmap.josm.command.PurgePrimitivesCommand; 
    17 import org.openstreetmap.josm.command.UndeletePrimitivesCommand; 
    1816import org.openstreetmap.josm.data.osm.DataSet; 
    19 import org.openstreetmap.josm.data.osm.Node; 
    2017import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    21 import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 
    22 import org.openstreetmap.josm.data.osm.Relation; 
    23 import org.openstreetmap.josm.data.osm.RelationMember; 
    24 import org.openstreetmap.josm.data.osm.Way; 
    25 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 
    26 import org.openstreetmap.josm.io.OsmApi; 
    27 import org.openstreetmap.josm.io.OsmApiException; 
    28 import org.openstreetmap.josm.io.OsmServerObjectReader; 
    29 import org.openstreetmap.josm.io.OsmTransferException; 
     18import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 
    3019import org.openstreetmap.josm.tools.Shortcut; 
    31 import org.xml.sax.SAXException; 
    3220 
    3321/** 
     
    4129 
    4230    /** 
    43      * Undelete a node which is already deleted on the server. The API 
    44      * doesn't offer a call for "undeleting" a node. We therefore create 
    45      * a clone of the node which we flag as new. On the next upload the 
    46      * server will assign the node a new id. 
    47      *  
    48      * @param node the node to undelete 
    49      */ 
    50     protected void  undeleteNode(Node node) { 
    51         UndeletePrimitivesCommand cmd = new UndeletePrimitivesCommand(node); 
    52         Main.main.undoRedo.add(cmd); 
    53     } 
    54  
    55     /** 
    56      * Undelete a way which is already deleted on the server. 
    57      *  
    58      * This method also checks whether there are additional nodes referred to by 
    59      * this way which are deleted on the server too. 
    60      *  
    61      * @param way the way to undelete 
    62      * @see #undeleteNode(Node) 
    63      */ 
    64     protected void undeleteWay(final Way way) { 
    65         class NodeGoneChecker extends PleaseWaitRunnable { 
    66  
    67             UndeletePrimitivesCommand cmd = null; 
    68  
    69             public NodeGoneChecker() { 
    70                 super(tr("Undeleting Way..."), false); 
    71             } 
    72  
    73             @Override 
    74             protected void cancel() { 
    75                 OsmApi.getOsmApi().cancel(); 
    76             } 
    77  
    78             @Override 
    79             protected void finish() { 
    80                 if (cmd != null) { 
    81                     Main.main.undoRedo.add(cmd); 
    82                 } 
    83             } 
    84  
    85             /** 
    86              * replies the subset of the node list which already 
    87              * have an assigned id 
    88              *  
    89              * @param way  the way 
    90              * @return the node list 
    91              */ 
    92             protected ArrayList<Node> getCandidateNodes(Way way) { 
    93                 ArrayList<Node> candidates = new ArrayList<Node>(); 
    94                 for (Node n : way.nodes) { 
    95                     if (n.id > 0 && ! candidates.contains(n)) { 
    96                         candidates.add(n); 
    97                     } 
    98                 } 
    99                 return candidates; 
    100             } 
    101  
    102             /** 
    103              * checks whether a specific node is deleted on the server 
    104              *  
    105              * @param n the node 
    106              * @return true, if the node is deleted; false, otherwise 
    107              * @throws OsmTransferException thrown, if an error occurs while communicating with the API 
    108              */ 
    109             protected boolean isGone(Node n) throws OsmTransferException { 
    110                 OsmServerObjectReader reader = new OsmServerObjectReader(n.id, OsmPrimitiveType.from(n), true); 
    111                 try { 
    112                     reader.parseOsm(); 
    113                 } catch(OsmApiException e) { 
    114                     if (e.getResponseCode() == HttpURLConnection.HTTP_GONE) 
    115                         return true; 
    116                     throw e; 
    117                 } catch(OsmTransferException e) { 
    118                     throw e; 
    119                 } 
    120                 return false; 
    121             } 
    122  
    123             /** 
    124              * displays a confirmation message. The user has to confirm that additional dependent 
    125              * nodes should be undeleted too. 
    126              *  
    127              * @param way  the way 
    128              * @param dependent a list of dependent nodes which have to be undelete too 
    129              * @return true, if the user confirms; false, otherwise 
    130              */ 
    131             protected boolean confirmUndeleteDependentPrimitives(Way way, ArrayList<OsmPrimitive> dependent) { 
    132                 String [] options = { 
    133                         tr("Yes, undelete them too"), 
    134                         tr("No, cancel operation") 
    135                 }; 
    136                 int ret = JOptionPane.showOptionDialog( 
    137                         Main.parent, 
    138                         tr("<html>There are {0} additional nodes used by way {1}<br>" 
    139                                 + "which are deleted on the server.<br>" 
    140                                 + "<br>" 
    141                                 + "Do you want to undelete these nodes too?</html>", 
    142                                 Long.toString(dependent.size()), Long.toString(way.id)), 
    143                                 tr("Undelete additional nodes?"), 
    144                                 JOptionPane.YES_NO_OPTION, 
    145                                 JOptionPane.QUESTION_MESSAGE, 
    146                                 null, 
    147                                 options, 
    148                                 options[0] 
    149                 ); 
    150  
    151                 switch(ret) { 
    152                 case JOptionPane.CLOSED_OPTION: return false; 
    153                 case JOptionPane.YES_OPTION: return true; 
    154                 case JOptionPane.NO_OPTION: return false; 
    155                 } 
    156                 return false; 
    157  
    158             } 
    159  
    160             @Override 
    161             protected void realRun() throws SAXException, IOException, OsmTransferException { 
    162                 ArrayList<Node> candidate = getCandidateNodes(way); 
    163                 ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>(); 
    164                 Main.pleaseWaitDlg.progress.setMinimum(0); 
    165                 Main.pleaseWaitDlg.progress.setMaximum(candidate.size()); 
    166  
    167                 for (int i=0; i<candidate.size();i++) { 
    168                     Node n = candidate.get(i); 
    169                     Main.pleaseWaitDlg.progress.setValue(i); 
    170                     Main.pleaseWaitDlg.currentAction.setText(tr("Checking whether node {0} is gone ...", n.id)); 
    171                     if (isGone(n)) { 
    172                         toDelete.add(n); 
    173                     } 
    174                 } 
    175                 if (toDelete.size() > 0) { 
    176                     if (!confirmUndeleteDependentPrimitives(way, toDelete)) 
    177                         return; 
    178                 } 
    179  
    180                 toDelete.add(way); 
    181                 cmd = new UndeletePrimitivesCommand(toDelete); 
    182             } 
    183         } 
    184  
    185         Main.worker.submit(new NodeGoneChecker()); 
    186     } 
    187  
    188     /** 
    189      * Undelete a relation which is already deleted on the server. 
    190      *  
    191      * This method  checks whether there are additional primitives referred to by 
    192      * this relation which are already deleted on the server. 
    193      *  
    194      * @param r the relation 
    195      * @see #undeleteNode(Node) 
    196      */ 
    197     protected void undeleteRelation(final Relation r) { 
    198         class RelationMemberGoneChecker extends PleaseWaitRunnable { 
    199  
    200             UndeletePrimitivesCommand cmd = null; 
    201  
    202             public RelationMemberGoneChecker() { 
    203                 super(tr("Undeleting relation..."), false); 
    204             } 
    205  
    206             @Override 
    207             protected void cancel() { 
    208                 OsmApi.getOsmApi().cancel(); 
    209             } 
    210  
    211             @Override 
    212             protected void finish() { 
    213                 if (cmd != null) { 
    214                     Main.main.undoRedo.add(cmd); 
    215                 } 
    216             } 
    217  
    218             protected ArrayList<OsmPrimitive> getCandidateRelationMembers(Relation r) { 
    219                 ArrayList<OsmPrimitive> candidates = new ArrayList<OsmPrimitive>(); 
    220                 for (RelationMember m : r.members) { 
    221                     if (m.member.id > 0 && !candidates.contains(m.member)) { 
    222                         candidates.add(m.member); 
    223                     } 
    224                 } 
    225                 return candidates; 
    226             } 
    227  
    228             protected boolean isGone(OsmPrimitive primitive) throws OsmTransferException { 
    229                 OsmServerObjectReader reader = new OsmServerObjectReader( 
    230                         primitive.id, 
    231                         OsmPrimitiveType.from(primitive), 
    232                         true); 
    233                 try { 
    234                     reader.parseOsm(); 
    235                 } catch(OsmApiException e) { 
    236                     if (e.getResponseCode() == HttpURLConnection.HTTP_GONE) 
    237                         return true; 
    238                     throw e; 
    239                 } catch(OsmTransferException e) { 
    240                     throw e; 
    241                 } 
    242                 return false; 
    243             } 
    244  
    245             protected boolean confirmUndeleteDependentPrimitives(Relation r, ArrayList<OsmPrimitive> dependent) { 
    246                 String [] options = { 
    247                         tr("Yes, undelete them too"), 
    248                         tr("No, cancel operation") 
    249                 }; 
    250                 int ret = JOptionPane.showOptionDialog( 
    251                         Main.parent, 
    252                         tr("<html>There are {0} additional primitives referred to by relation {1}<br>" 
    253                                 + "which are deleted on the server.<br>" 
    254                                 + "<br>" 
    255                                 + "Do you want to undelete them too?</html>", 
    256                                 Long.toString(dependent.size()), Long.toString(r.id)), 
    257                                 tr("Undelete dependent primitives?"), 
    258                                 JOptionPane.YES_NO_OPTION, 
    259                                 JOptionPane.QUESTION_MESSAGE, 
    260                                 null, 
    261                                 options, 
    262                                 options[0] 
    263                 ); 
    264  
    265                 switch(ret) { 
    266                 case JOptionPane.CLOSED_OPTION: return false; 
    267                 case JOptionPane.YES_OPTION: return true; 
    268                 case JOptionPane.NO_OPTION: return false; 
    269                 } 
    270                 return false; 
    271  
    272             } 
    273  
    274             @Override 
    275             protected void realRun() throws SAXException, IOException, OsmTransferException { 
    276                 ArrayList<OsmPrimitive> candidate = getCandidateRelationMembers(r); 
    277                 ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>(); 
    278                 Main.pleaseWaitDlg.progress.setMinimum(0); 
    279                 Main.pleaseWaitDlg.progress.setMaximum(candidate.size()); 
    280  
    281                 for (int i=0; i<candidate.size();i++) { 
    282                     OsmPrimitive primitive = candidate.get(i); 
    283                     Main.pleaseWaitDlg.progress.setValue(i); 
    284                     Main.pleaseWaitDlg.currentAction.setText(tr("Checking whether primitive {0} is gone ...", primitive.id)); 
    285                     if (isGone(primitive)) { 
    286                         toDelete.add(primitive); 
    287                     } 
    288                 } 
    289                 if (toDelete.size() > 0) { 
    290                     if (!confirmUndeleteDependentPrimitives(r, toDelete)) 
    291                         return; 
    292                 } 
    293  
    294                 toDelete.add(r); 
    295                 cmd = new UndeletePrimitivesCommand(toDelete); 
    296             } 
    297         } 
    298  
    299         Main.worker.submit(new RelationMemberGoneChecker()); 
    300     } 
    301  
    302     /** 
    303      * User has decided to keep his local version of a primitive which had been deleted 
    304      * on the server 
    305      *  
    306      * @param id the primitive id 
    307      */ 
    308     protected void handlePrimitiveGoneKeepMine(long id) { 
    309         OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id); 
    310         if (primitive instanceof Node) { 
    311             undeleteNode((Node)primitive); 
    312         } else if (primitive instanceof Way) { 
    313             undeleteWay((Way)primitive); 
    314         } else if (primitive instanceof Relation) { 
    315             undeleteRelation((Relation)primitive); 
    316         } 
    317     } 
    318  
    319     /** 
    320      * User has decided to delete his local version of a primitive which had been deleted 
    321      * on the server 
    322      *  
    323      * @param id the primitive id 
    324      */ 
    325     protected void handlePrimitiveGoneDeleteMine(long id) { 
    326         OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id); 
    327         PurgePrimitivesCommand cmd = new PurgePrimitivesCommand(primitive); 
    328         Main.main.undoRedo.add(cmd); 
    329         Main.map.mapView.repaint(); 
    330     } 
    331  
    332     /** 
    33331     * handle an exception thrown because a primitive was deleted on the server 
    33432     *  
     
    33634     */ 
    33735    protected void handlePrimitiveGoneException(long id) { 
    338         Object[] options = new Object[] { 
    339                 tr("Keep mine"), 
    340                 tr("Delete mine"), 
    341                 tr("Cancel") 
    342         }; 
    343         Object defaultOption = options[0]; 
    344         String msg =  tr("<html>The OSM primitive with id <strong>{0}</strong> has been deleted<br>" 
    345                 + "on the server by another mapper.<br>" 
    346                 + "<br>" 
    347                 + "Click <strong>{1}</strong> to keep your primitive and ignore the deleted state.<br>" 
    348                 + "Your primitive will be assigend a new id.<br>" 
    349                 + "Click <strong>{2}</strong> to accept the state on the server and to delete your primitive.<br>" 
    350                 + "Click <strong>{3}</strong> to cancel.<br>", 
    351                 id, options[0], options[1], options[2] 
    352         ); 
    353         int ret = JOptionPane.showOptionDialog( 
    354                 null, 
    355                 msg, 
    356                 tr("Primitive deleted on the server"), 
    357                 JOptionPane.YES_NO_CANCEL_OPTION, 
    358                 JOptionPane.ERROR_MESSAGE, 
    359                 null, 
    360                 options, 
    361                 defaultOption 
    362         ); 
    363         switch(ret) { 
    364         case JOptionPane.CLOSED_OPTION: return; 
    365         case JOptionPane.CANCEL_OPTION: return; 
    366         case 0: handlePrimitiveGoneKeepMine(id); break; 
    367         case 1: handlePrimitiveGoneDeleteMine(id); break; 
    368         default: 
    369             // should not happen 
    370             throw new IllegalStateException(tr("unexpected return value. Got {0}", ret)); 
     36        MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader(); 
     37        reader.append(Main.main.editLayer().data,id); 
     38        DataSet ds = null; 
     39        try { 
     40            ds = reader.parseOsm(); 
     41        } catch(Exception e) { 
     42            handleUpdateException(e); 
     43            return; 
    37144        } 
     45        Main.main.editLayer().mergeFrom(ds); 
    37246    } 
    37347 
     
    37852     * @param e the exception 
    37953     */ 
    380     protected void handleUpdateException(long id, Exception e) { 
    381         if (e instanceof OsmApiException) { 
    382             OsmApiException ex = (OsmApiException)e; 
    383             // if the primitive was deleted on the server ask the user 
    384             // whether he wants to undelete it 
    385             // 
    386             if (ex.getResponseCode() == HttpURLConnection.HTTP_GONE) { 
    387                 handlePrimitiveGoneException(id); 
    388                 return; 
    389             } 
    390         } 
    391  
     54    protected void handleUpdateException(Exception e) { 
    39255        e.printStackTrace(); 
    39356        JOptionPane.showMessageDialog( 
     
    40669        JOptionPane.showMessageDialog( 
    40770                Main.parent, 
    408                 tr("Could not find primitive with id {0} in the current dataset", id), 
     71                tr("Could not find primitive with id {0} in the current dataset", new Long(id).toString()), 
    40972                tr("Missing primitive"), 
    41073                JOptionPane.ERROR_MESSAGE 
     
    41376 
    41477    /** 
    415      * Updates the primitive with id <code>id</code> with the current state kept on the server. 
    41678     *  
    417      * @param id 
     79     *  
     80     *  
    41881     */ 
    419     public void updatePrimitive(long id) { 
    420         OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id); 
    421         if (primitive == null) { 
    422             handleMissingPrimitive(id); 
    423             return; 
    424         } 
    425         OsmServerObjectReader reader = new OsmServerObjectReader( 
    426                 id, 
    427                 OsmPrimitiveType.from(primitive), 
    428                 true); 
     82    public void updatePrimitives(Collection<OsmPrimitive> selection) { 
     83        MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader(); 
     84        reader.append(selection); 
    42985        DataSet ds = null; 
    43086        try { 
    43187            ds = reader.parseOsm(); 
    43288        } catch(Exception e) { 
    433             handleUpdateException(id, e); 
     89            handleUpdateException(e); 
    43490            return; 
    43591        } 
     
    43793    } 
    43894 
     95    public void updatePrimitive(long id) { 
     96        OsmPrimitive primitive = Main.main.editLayer().data.getPrimitiveById(id); 
     97        Set<OsmPrimitive> s = new HashSet<OsmPrimitive>(); 
     98        s.add(primitive); 
     99        updatePrimitives(s); 
     100    } 
    439101 
    440102    public UpdateSelectionAction() { 
     
    461123            return; 
    462124        } 
    463  
    464         // check whether the current selection has an acceptable range. 
    465         // We don't want to hammer the API with hundreds of individual 
    466         // GET requests. 
    467         // 
    468         if (selection.size() > DEFAULT_MAX_SIZE_UPDATE_SELECTION) { 
    469             JOptionPane.showMessageDialog( 
    470                     Main.parent, 
    471                     tr("<html>There are  <strong>{0}</strong> primitives <br>" 
    472                             + "selected for individual update. Please reduce the selection<br>" 
    473                             + "to max. {1} primitives.</html>", 
    474                             selection.size(), DEFAULT_MAX_SIZE_UPDATE_SELECTION 
    475                     ), 
    476                     tr("Selection too big"), 
    477                     JOptionPane.WARNING_MESSAGE 
    478             ); 
    479             return; 
    480         } 
    481         for(OsmPrimitive primitive : selection) { 
    482             // FIXME: users should be able to abort this loop 
    483             // 
    484             updatePrimitive(primitive.id); 
    485         } 
     125        updatePrimitives(selection); 
    486126    } 
    487127} 
Note: See TracChangeset for help on using the changeset viewer.