Changeset 1690 in josm


Ignore:
Timestamp:
Jun 23, 2009 10:03:37 PM (4 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

Location:
trunk/src/org/openstreetmap/josm
Files:
2 added
21 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} 
  • trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskList.java

    r1682 r1690  
    128128    protected void handlePotentiallyDeletedPrimitives(Set<Long> potentiallyDeleted) { 
    129129        String [] options = { 
    130                 "Check individually", 
     130                "Check on the server", 
    131131                "Ignore" 
    132132        }; 
     
    138138                + "conflict.<br>" 
    139139                + "<br>" 
    140                 + "Click <strong>{1}</strong> to check these primitives individually.<br>" 
     140                + "Click <strong>{1}</strong> to check the state of these primitives<br>" 
     141                + "on the server.<br>" 
    141142                + "Click <strong>{2}</strong> to ignore.<br>" 
    142143                + "</html>", 
  • trunk/src/org/openstreetmap/josm/command/DeletedStateConflictResolveCommand.java

    r1654 r1690  
    6666 
    6767        if (decision.equals(MergeDecisionType.KEEP_MINE)) { 
    68             // do nothing 
     68            if (my.deleted) { 
     69                // because my was involved in a conflict it my still be referred 
     70                // to from a way or a relation. Fix this now. 
     71                // 
     72                Main.main.editLayer().data.unlinkReferencesToPrimitive(my); 
     73            } 
    6974        } else if (decision.equals(MergeDecisionType.KEEP_THEIR)) { 
    70             my.deleted = their.deleted; 
     75            if (their.deleted) { 
     76                Main.main.editLayer().data.unlinkReferencesToPrimitive(my); 
     77                my.delete(true); 
     78            } else { 
     79                my.deleted = their.deleted; 
     80            } 
    7181        } else 
    7282            // should not happen 
  • trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java

    r1670 r1690  
    66import java.util.ArrayList; 
    77import java.util.Collection; 
     8import java.util.HashMap; 
    89import java.util.List; 
     10import java.util.Map; 
    911 
    1012import javax.swing.JLabel; 
     
    3335 */ 
    3436public class PurgePrimitivesCommand extends Command{ 
     37 
    3538 
    3639    /** 
     
    141144    private ArrayList<OsmParentChildPair> pairs; 
    142145 
     146    private Map<OsmPrimitive, OsmPrimitive> resolvedConflicts; 
     147 
    143148    /** 
    144149     * constructor 
     
    149154        purgedPrimitives = new ArrayList<OsmPrimitive>(); 
    150155        pairs = new ArrayList<OsmParentChildPair>(); 
     156        resolvedConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(); 
    151157    } 
    152158 
     
    180186            if (pair.getParent() instanceof Way) { 
    181187                Way w = (Way)pair.getParent(); 
    182                 System.out.println("removing reference from way " + w.id); 
     188                System.out.println(tr("removing reference from way {0}",w.id)); 
    183189                w.nodes.remove(primitive); 
    184190                // if a way ends up with less than two node we 
     
    194200            } else if (pair.getParent() instanceof Relation) { 
    195201                Relation r = (Relation)pair.getParent(); 
    196                 System.out.println("removing reference from relation " + r.id); 
     202                System.out.println(tr("removing reference from relation {0}",r.id)); 
    197203                r.removeMembersFor(primitive); 
    198204            } 
     
    220226            } 
    221227            purgedPrimitives.add(toPurge); 
     228            if (Main.map.conflictDialog.conflicts.containsKey(toPurge)) { 
     229                resolvedConflicts.put(toPurge, Main.map.conflictDialog.conflicts.get(toPurge)); 
     230                Main.map.conflictDialog.removeConflictForPrimitive(toPurge); 
     231            } 
    222232        } 
    223233        return super.executeCommand(); 
     
    236246    @Override 
    237247    public void undoCommand() { 
     248 
     249        // restore purged primitives 
     250        // 
    238251        for (OsmPrimitive purged : purgedPrimitives) { 
    239252            Main.ds.addPrimitive(purged); 
     253        } 
     254 
     255        // restore conflicts 
     256        // 
     257        for (OsmPrimitive primitive : resolvedConflicts.keySet()) { 
     258            Main.map.conflictDialog.addConflict(primitive, resolvedConflicts.get(primitive)); 
    240259        } 
    241260 
  • trunk/src/org/openstreetmap/josm/command/UndeletePrimitivesCommand.java

    r1670 r1690  
    66import java.util.ArrayList; 
    77import java.util.Collection; 
     8import java.util.HashMap; 
     9import java.util.Map; 
    810 
    911import javax.swing.JLabel; 
     
    1113import javax.swing.tree.MutableTreeNode; 
    1214 
     15import org.openstreetmap.josm.Main; 
    1316import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    1417import org.openstreetmap.josm.tools.ImageProvider; 
     
    2427    /** the node to undelete */ 
    2528    private ArrayList<OsmPrimitive> toUndelete; 
     29    private Map<OsmPrimitive,OsmPrimitive> resolvedConflicts; 
    2630 
     31    protected UndeletePrimitivesCommand() { 
     32        toUndelete = new ArrayList<OsmPrimitive>(); 
     33        resolvedConflicts = new HashMap<OsmPrimitive, OsmPrimitive>(); 
     34    } 
    2735    /** 
    2836     * constructor 
     
    3038     */ 
    3139    public UndeletePrimitivesCommand(OsmPrimitive node) { 
    32         toUndelete = new ArrayList<OsmPrimitive>(); 
     40        this(); 
    3341        toUndelete.add(node); 
    3442    } 
     
    3947     */ 
    4048    public UndeletePrimitivesCommand(OsmPrimitive ... toUndelete) { 
    41         this.toUndelete = new ArrayList<OsmPrimitive>(); 
     49        this(); 
    4250        for (int i=0; i < toUndelete.length; i++) { 
    4351            this.toUndelete.add(toUndelete[i]); 
     
    5058     */ 
    5159    public UndeletePrimitivesCommand(Collection<OsmPrimitive> toUndelete) { 
    52         this.toUndelete = new ArrayList<OsmPrimitive>(); 
     60        this(); 
    5361        this.toUndelete.addAll(toUndelete); 
    5462    } 
     
    7078        super.executeCommand(); 
    7179        for(OsmPrimitive primitive: toUndelete) { 
     80            if (Main.map.conflictDialog.conflicts.containsKey(primitive)) { 
     81                resolvedConflicts.put(primitive, Main.map.conflictDialog.conflicts.get(primitive)); 
     82                Main.map.conflictDialog.removeConflictForPrimitive(primitive); 
     83            } 
    7284            primitive.id = 0; 
    7385        } 
     
    8092        modified.addAll(toUndelete); 
    8193    } 
     94    @Override 
     95    public void undoCommand() { 
     96        super.undoCommand(); 
     97 
     98        for (OsmPrimitive my: resolvedConflicts.keySet()) { 
     99            if (!Main.map.conflictDialog.conflicts.containsKey(my)) { 
     100                Main.map.conflictDialog.addConflict(my, resolvedConflicts.get(my)); 
     101            } 
     102        } 
     103    } 
    82104} 
  • trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java

    r1670 r1690  
    1111 
    1212import org.openstreetmap.josm.Main; 
    13 import org.openstreetmap.josm.data.osm.Node; 
    1413import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    1514import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 
    16 import org.openstreetmap.josm.data.osm.Relation; 
    17 import org.openstreetmap.josm.data.osm.Way; 
    1815import org.openstreetmap.josm.tools.ImageProvider; 
    1916 
  • trunk/src/org/openstreetmap/josm/data/osm/DataSet.java

    r1677 r1690  
    88import java.util.HashSet; 
    99import java.util.HashMap; 
     10import java.util.Iterator; 
    1011import java.util.LinkedList; 
    1112import java.util.List; 
     
    8081        Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>(); 
    8182        for (OsmPrimitive osm : allPrimitives()) 
    82             if (!osm.deleted) { 
     83            if (osm.visible && !osm.deleted) { 
    8384                o.add(osm); 
    8485            } 
     
    8990        Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>(); 
    9091        for (OsmPrimitive osm : allPrimitives()) 
    91             if (!osm.deleted && !osm.incomplete) { 
     92            if (osm.visible && !osm.deleted && !osm.incomplete) { 
    9293                o.add(osm); 
    9394            } 
     
    9899        Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>(); 
    99100        for (OsmPrimitive osm : allPrimitives()) 
    100             if (!osm.deleted && !osm.incomplete && !(osm instanceof Relation)) { 
     101            if (osm.visible && !osm.deleted && !osm.incomplete && !(osm instanceof Relation)) { 
    101102                o.add(osm); 
    102103            } 
     
    311312        return ret; 
    312313    } 
     314 
     315    protected void deleteWay(Way way) { 
     316        way.nodes.clear(); 
     317        way.delete(true); 
     318    } 
     319 
     320    /** 
     321     * removes all references from ways in this dataset to a particular node 
     322     *  
     323     * @param node the node 
     324     */ 
     325    public void unlinkNodeFromWays(Node node) { 
     326        for (Way way: ways) { 
     327            if (way.nodes.contains(node)) { 
     328                way.nodes.remove(node); 
     329                if (way.nodes.size() < 2) { 
     330                    deleteWay(way); 
     331                } 
     332            } 
     333        } 
     334    } 
     335 
     336    /** 
     337     * removes all references from relations in this dataset  to this primitive 
     338     *  
     339     * @param primitive the primitive 
     340     */ 
     341    public void unlinkPrimitiveFromRelations(OsmPrimitive primitive) { 
     342        for (Relation relation : relations) { 
     343            Iterator<RelationMember> it = relation.members.iterator(); 
     344            while(it.hasNext()) { 
     345                RelationMember member = it.next(); 
     346                if (member.member.equals(primitive)) { 
     347                    it.remove(); 
     348                } 
     349            } 
     350        } 
     351    } 
     352 
     353    /** 
     354     * removes all references from from other primitives  to the 
     355     * referenced primitive 
     356     *  
     357     * @param referencedPrimitive the referenced primitive 
     358     */ 
     359    public void unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) { 
     360        if (referencedPrimitive instanceof Node) { 
     361            unlinkNodeFromWays((Node)referencedPrimitive); 
     362            unlinkPrimitiveFromRelations(referencedPrimitive); 
     363        } else { 
     364            unlinkPrimitiveFromRelations(referencedPrimitive); 
     365        } 
     366    } 
    313367} 
  • trunk/src/org/openstreetmap/josm/data/osm/Node.java

    r1640 r1690  
    3131 
    3232    public final void setEastNorth(EastNorth eastNorth) { 
    33        this.eastNorth = eastNorth; 
    34        this.coor = Main.proj.eastNorth2latlon(eastNorth); 
     33        this.eastNorth = eastNorth; 
     34        this.coor = Main.proj.eastNorth2latlon(eastNorth); 
    3535    } 
    3636 
     
    8787    } 
    8888 
     89    /** 
     90     * @deprecated 
     91     * @see #hasEqualSemanticAttributes(OsmPrimitive) 
     92     * @see #hasEqualTechnicalAttributes(OsmPrimitive) 
     93     */ 
     94    @Deprecated 
    8995    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 
    9096        if (osm instanceof Node) { 
     
    99105    } 
    100106 
     107    @Override 
     108    public boolean hasEqualSemanticAttributes(OsmPrimitive other) { 
     109        if (other == null || ! (other instanceof Node) ) 
     110            return false; 
     111        if (! super.hasEqualSemanticAttributes(other)) 
     112            return false; 
     113        Node n = (Node)other; 
     114        if (coor == null && n.coor == null) 
     115            return true; 
     116        else if (coor != null && n.coor != null) 
     117            return coor.equals(n.coor); 
     118        else 
     119            return false; 
     120    } 
     121 
    101122    public int compareTo(OsmPrimitive o) { 
    102123        return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1; 
    103124    } 
    104125 
     126    @Override 
    105127    public String getName() { 
    106128        String name; 
     
    109131        } else { 
    110132            name = get("name"); 
    111             if (name == null) 
     133            if (name == null) { 
    112134                name = id == 0 ? tr("node") : ""+id; 
     135            } 
    113136            name += " (" + coor.latToString(mCord) + ", " + coor.lonToString(mCord) + ")"; 
    114137        } 
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r1530 r1690  
    4343    public void putError(String text, Boolean isError) 
    4444    { 
    45         if(errors == null) 
     45        if(errors == null) { 
    4646            errors = new ArrayList<String>(); 
     47        } 
    4748        String s = isError ? tr("Error: {0}", text) : tr("Warning: {0}", text); 
    4849        errors.add(s); 
     
    9192     * Visibility status as specified by the server. The visible attribute was 
    9293     * introduced with the 0.4 API to be able to communicate deleted objects 
    93      * (they will have visible=false). Currently JOSM does never deal with 
    94      * these, so this is really for future use only. 
     94     * (they will have visible=false). 
    9595     */ 
    9696    public boolean visible = true; 
     
    195195    @Override public boolean equals(Object obj) { 
    196196        if (id == 0) return obj == this; 
    197         if (obj instanceof OsmPrimitive) { // not null too 
     197        if (obj instanceof OsmPrimitive) 
    198198            return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass(); 
    199         } 
    200199        return false; 
    201200    } 
     
    226225     */ 
    227226    public final void put(String key, String value) { 
    228         if (value == null) 
     227        if (value == null) { 
    229228            remove(key); 
    230         else { 
    231             if (keys == null) 
     229        } else { 
     230            if (keys == null) { 
    232231                keys = new HashMap<String, String>(); 
     232            } 
    233233            keys.put(key, value); 
    234234        } 
     
    241241        if (keys != null) { 
    242242            keys.remove(key); 
    243             if (keys.isEmpty()) 
     243            if (keys.isEmpty()) { 
    244244                keys = null; 
     245            } 
    245246        } 
    246247        mappaintStyle = null; 
     
    280281        version = osm.version; 
    281282        incomplete = osm.incomplete; 
     283        visible = osm.visible; 
    282284        clearCached(); 
    283285        clearErrors(); 
     
    288290     * but for the whole object (for conflict resolving) 
    289291     * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared 
    290      */ 
     292     *  
     293     * @deprecated 
     294     * @see #hasEqualSemanticAttributes(OsmPrimitive) 
     295     * @see #hasEqualTechnicalAttributes(OsmPrimitive) 
     296     */ 
     297    @Deprecated 
    291298    public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 
    292299        return id == osm.id 
    293300        && incomplete == osm.incomplete 
    294301        && deleted == osm.deleted 
    295         && (semanticOnly || (modified == osm.modified 
    296          && timestamp == osm.timestamp 
    297          && version == osm.version 
    298          && visible == osm.visible 
    299          && (user == null ? osm.user==null : user==osm.user))) 
     302        && (semanticOnly || ( 
     303                modified == osm.modified 
     304                && timestamp == osm.timestamp 
     305                && version == osm.version 
     306                && visible == osm.visible 
     307                && (user == null ? osm.user==null : user==osm.user)) 
     308        ) 
    300309        && (keys == null ? osm.keys==null : keys.equals(osm.keys)); 
     310    } 
     311 
     312    /** 
     313     * Replies true if this primitive and other are equal with respect to their 
     314     * semantic attributes. 
     315     * <ol> 
     316     *   <li>equal id</ol> 
     317     *   <li>both are complete or both are incomplete</li> 
     318     *   <li>both have the same tags</li> 
     319     * </ol> 
     320     * @param other 
     321     * @return true if this primitive and other are equal with respect to their 
     322     * semantic attributes. 
     323     */ 
     324    public boolean hasEqualSemanticAttributes(OsmPrimitive other) { 
     325        if (id != other.id) 
     326            return false; 
     327        if (incomplete && ! other.incomplete || !incomplete  && other.incomplete) 
     328            return false; 
     329        return (keys == null ? other.keys==null : keys.equals(other.keys)); 
     330    } 
     331 
     332    /** 
     333     * Replies true if this primitive and other are equal with respect to their 
     334     * technical attributes. The attributes: 
     335     * <ol> 
     336     *   <li>deleted</ol> 
     337     *   <li>modified</ol> 
     338     *   <li>timestamp</ol> 
     339     *   <li>version</ol> 
     340     *   <li>visible</ol> 
     341     *   <li>user</ol> 
     342     * </ol> 
     343     * have to be equal 
     344     * @param other the other primitive 
     345     * @return true if this primitive and other are equal with respect to their 
     346     * technical attributes 
     347     */ 
     348    public boolean hasEqualTechnicalAttributes(OsmPrimitive other) { 
     349        if (other == null) return false; 
     350 
     351        return 
     352        deleted == other.deleted 
     353        && modified == other.modified 
     354        && timestamp == other.timestamp 
     355        && version == other.version 
     356        && visible == other.visible 
     357        && (user == null ? other.user==null : user==other.user); 
    301358    } 
    302359 
     
    312369        if (keys != null) { 
    313370            for (Entry<String,String> e : keys.entrySet()) { 
    314                 if (!uninteresting.contains(e.getKey())) { 
     371                if (!uninteresting.contains(e.getKey())) 
    315372                    return true; 
    316                 } 
    317373            } 
    318374        } 
     
    327383        if (keys != null) { 
    328384            for (Entry<String,String> e : keys.entrySet()) { 
    329                 if (directionKeys.contains(e.getKey())) { 
     385                if (directionKeys.contains(e.getKey())) 
    330386                    return true; 
    331                 } 
    332387            } 
    333388        } 
  • trunk/src/org/openstreetmap/josm/data/osm/Relation.java

    r1677 r1690  
    7070    } 
    7171 
     72    @Deprecated 
    7273    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 
    7374        return osm instanceof Relation ? super.realEqual(osm, semanticOnly) && members.equals(((Relation)osm).members) : false; 
     75    } 
     76 
     77    @Override 
     78    public boolean hasEqualSemanticAttributes(OsmPrimitive other) { 
     79        if (other == null || ! (other instanceof Relation) ) 
     80            return false; 
     81        if (! super.hasEqualSemanticAttributes(other)) 
     82            return false; 
     83        Relation r = (Relation)other; 
     84        return members.equals(r.members); 
    7485    } 
    7586 
  • trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java

    r1169 r1690  
    3333 
    3434    @Override public boolean equals(Object other) { 
    35         if (!(other instanceof RelationMember)) return false; 
     35        if (other == null || !(other instanceof RelationMember)) return false; 
    3636        RelationMember otherMember = (RelationMember) other; 
    3737        return otherMember.role.equals(role) && otherMember.member.equals(member); 
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r1677 r1690  
    3939    public void visitNodes(Visitor v) { 
    4040        if (incomplete) return; 
    41         for (Node n : this.nodes) 
     41        for (Node n : this.nodes) { 
    4242            v.visit(n); 
     43        } 
    4344    } 
    4445 
     
    100101    } 
    101102 
     103    @Deprecated 
    102104    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) { 
    103105        return osm instanceof Way ? super.realEqual(osm, semanticOnly) && nodes.equals(((Way)osm).nodes) : false; 
     106    } 
     107 
     108    @Override 
     109    public boolean hasEqualSemanticAttributes(OsmPrimitive other) { 
     110        if (other == null || ! (other instanceof Way) ) 
     111            return false; 
     112        if (! super.hasEqualSemanticAttributes(other)) 
     113            return false; 
     114        Way w = (Way)other; 
     115        return nodes.equals(w.nodes); 
    104116    } 
    105117 
     
    110122    } 
    111123 
     124    @Override 
    112125    public String getName() { 
    113126        String name; 
     
    116129        } else { 
    117130            name = get("name"); 
    118             if (name == null) name = get("ref"); 
     131            if (name == null) { 
     132                name = get("ref"); 
     133            } 
    119134            if (name == null) { 
    120135                name = 
    121136                    (get("highway") != null) ? tr("highway") : 
    122                     (get("railway") != null) ? tr("railway") : 
    123                     (get("waterway") != null) ? tr("waterway") : 
    124                     (get("landuse") != null) ? tr("landuse") : ""; 
     137                        (get("railway") != null) ? tr("railway") : 
     138                            (get("waterway") != null) ? tr("waterway") : 
     139                                (get("landuse") != null) ? tr("landuse") : ""; 
    125140            } 
    126141 
     
    128143            String nodes = trn("{0} node", "{0} nodes", nodesNo, nodesNo); 
    129144            name += (name.length() > 0) ? " ("+nodes+")" : nodes; 
    130             if(errors != null) 
     145            if(errors != null) { 
    131146                name = "*"+name; 
     147            } 
    132148        } 
    133149        return name; 
     
    138154        boolean closed = (lastNode() == n && firstNode() == n); 
    139155        int i; 
    140         while ((i = nodes.indexOf(n)) >= 0) 
     156        while ((i = nodes.indexOf(n)) >= 0) { 
    141157            nodes.remove(i); 
     158        } 
    142159        i = nodes.size(); 
    143         if (closed && i > 2) // close again 
     160        if (closed && i > 2) { 
    144161            addNode(firstNode()); 
    145         // prevent closed ways with less than 3 different nodes 
    146         else if (i >= 2 && i <= 3 && nodes.get(0) == nodes.get(i-1)) 
     162        } else if (i >= 2 && i <= 3 && nodes.get(0) == nodes.get(i-1)) { 
    147163            nodes.remove(i-1); 
     164        } 
    148165    } 
    149166 
     
    151168        if (incomplete) return; 
    152169        for(OsmPrimitive p : selection) { 
    153            if (p instanceof Node) { 
    154                removeNode((Node)p); 
    155            } 
    156        } 
     170            if (p instanceof Node) { 
     171                removeNode((Node)p); 
     172            } 
     173        } 
    157174    } 
    158175 
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java

    r1677 r1690  
    22package org.openstreetmap.josm.data.osm.visitor; 
    33 
     4import static org.openstreetmap.josm.tools.I18n.tr; 
     5 
    46import java.util.Collection; 
    5 import java.util.Date; 
    67import java.util.HashMap; 
    78import java.util.Iterator; 
    89import java.util.LinkedList; 
    910import java.util.Map; 
     11import java.util.logging.Logger; 
    1012 
    1113import org.openstreetmap.josm.data.osm.DataSet; 
     14import org.openstreetmap.josm.data.osm.Node; 
     15import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    1216import org.openstreetmap.josm.data.osm.Relation; 
    1317import org.openstreetmap.josm.data.osm.RelationMember; 
    14 import org.openstreetmap.josm.data.osm.Node; 
    15 import org.openstreetmap.josm.data.osm.OsmPrimitive; 
    1618import org.openstreetmap.josm.data.osm.Way; 
    1719 
    1820/** 
    19  * A visitor that get a data set at construction time and merge every visited object 
     21 * A visitor that gets a data set at construction time and merges every visited object 
    2022 * into it. 
    2123 * 
    2224 * @author imi 
     25 * @author Gubaer 
    2326 */ 
    2427public class MergeVisitor extends AbstractVisitor { 
     28    private static Logger logger = Logger.getLogger(MergeVisitor.class.getName()); 
    2529 
    2630    /** 
     
    2832     * round than merged) 
    2933     */ 
    30     public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>(); 
    31  
    32     private final DataSet ds; 
    33     private final DataSet mergeds; 
     34    private Map<OsmPrimitive, OsmPrimitive> conflicts; 
     35 
     36    private final DataSet myDataSet; 
     37    private final DataSet theirDataSet; 
    3438 
    3539    private final HashMap<Long, Node> nodeshash = new HashMap<Long, Node>(); 
     
    4246     * in ds.nodes instead. 
    4347     */ 
    44     private final Map<OsmPrimitive, OsmPrimitive> merged 
    45         = new HashMap<OsmPrimitive, OsmPrimitive>(); 
    46  
    47     public MergeVisitor(DataSet ds, DataSet mergeds) { 
    48         this.ds = ds; 
    49         this.mergeds = mergeds; 
    50  
    51         for (Node n : ds.nodes) if (n.id != 0) nodeshash.put(n.id, n); 
    52         for (Way w : ds.ways) if (w.id != 0) wayshash.put(w.id, w); 
    53         for (Relation r : ds.relations) if (r.id != 0) relshash.put(r.id, r); 
    54     } 
    55  
    56     private <P extends OsmPrimitive> void genMerge(P other, 
    57             Collection<P> myprims, Collection<P> mergeprims, 
    58             HashMap<Long, P> primhash) { 
    59         // 1. Try to find an identical prim with the same id. 
    60         if (mergeById(myprims, primhash, other)) 
    61             return; 
    62  
    63         // 2. Try to find a prim we can merge with the prim from the other ds. 
    64         for (P my : myprims) { 
    65             // LinkedList.contains calls equal, and OsmPrimitive.equal 
    66             // compares just the id. 
    67             if (match(my, other) && !mergeprims.contains(my)) { 
    68                 merged.put(other, my); 
    69                 mergeCommon(my, other); 
     48    private Map<OsmPrimitive, OsmPrimitive> merged; 
     49 
     50    /** 
     51     * constructor 
     52     *  
     53     * The visitor will merge <code>theirDataSet</code> onto <code>myDataSet</code> 
     54     *  
     55     * @param myDataSet  dataset with my primitives 
     56     * @param theirDataSet dataset with their primitives. 
     57     */ 
     58    public MergeVisitor(DataSet myDataSet, DataSet theirDataSet) { 
     59        this.myDataSet = myDataSet; 
     60        this.theirDataSet = theirDataSet; 
     61 
     62        for (Node n : myDataSet.nodes) if (n.id != 0) { 
     63            nodeshash.put(n.id, n); 
     64        } 
     65        for (Way w : myDataSet.ways) if (w.id != 0) { 
     66            wayshash.put(w.id, w); 
     67        } 
     68        for (Relation r : myDataSet.relations) if (r.id != 0) { 
     69            relshash.put(r.id, r); 
     70        } 
     71        conflicts = new HashMap<OsmPrimitive, OsmPrimitive>(); 
     72        merged = new HashMap<OsmPrimitive, OsmPrimitive>(); 
     73    } 
     74 
     75    /** 
     76     * Merges a primitive <code>other</code> of type <P> onto my primitives. 
     77     *  
     78     * If other.id != 0 it tries to merge it with an corresponding primitive from 
     79     * my dataset with the same id. If this is not possible a conflict is remembered 
     80     * in {@see #conflicts}. 
     81     *  
     82     * If other.id == 0 it tries to find a primitive in my dataset with id == 0 which 
     83     * is semantically equal. If it finds one it merges its technical attributes onto 
     84     * my primitive. 
     85     *  
     86     * @param <P>  the type of the other primitive 
     87     * @param other  the other primitive 
     88     * @param myPrimitives the collection of my relevant primitives (i.e. only my 
     89     *    primitives of the same type) 
     90     * @param otherPrimitives  the collection of the other primitives 
     91     * @param primitivesWithDefinedIds the collection of my primitives with an 
     92     *   assigned id (i.e. id != 0) 
     93     */ 
     94    protected <P extends OsmPrimitive> void mergePrimitive(P other, 
     95            Collection<P> myPrimitives, Collection<P> otherPrimitives, 
     96            HashMap<Long, P> primitivesWithDefinedIds) { 
     97 
     98        if (other.id > 0 ) { 
     99            // try to merge onto a matching primitive with the same 
     100            // defined id 
     101            // 
     102            if (mergeById(myPrimitives, primitivesWithDefinedIds, other)) 
    70103                return; 
    71             } 
    72         } 
    73  
    74         // 3. No idea how to merge that.  Simply add it unchanged. 
    75         myprims.add(other); 
     104        } else { 
     105            // try to merge onto a primitive with which has no id assigned 
     106            // yet but which is equal in its semantic attributes 
     107            // 
     108            for (P my : myPrimitives) { 
     109                if (my.id >0 ) { 
     110                    continue; 
     111                } 
     112                if (my.hasEqualSemanticAttributes(other)) { 
     113                    // copy the technical attributes from their 
     114                    // version 
     115                    if (other.deleted) { 
     116                        myDataSet.unlinkReferencesToPrimitive(my); 
     117                        my.delete(true); 
     118                    } 
     119                    my.visible = other.visible; 
     120                    my.user = other.user; 
     121                    my.setTimestamp(other.getTimestamp()); 
     122                    my.modified = other.modified; 
     123                    merged.put(other, my); 
     124                    return; 
     125                } 
     126            } 
     127        } 
     128        // If we get here we didn't find a suitable primitive in 
     129        // my dataset. Just add other to my dataset. 
     130        // 
     131        myPrimitives.add(other); 
    76132    } 
    77133 
    78134    public void visit(Node other) { 
    79         genMerge(other, ds.nodes, mergeds.nodes, nodeshash); 
     135        mergePrimitive(other, myDataSet.nodes, theirDataSet.nodes, nodeshash); 
    80136    } 
    81137 
    82138    public void visit(Way other) { 
    83139        fixWay(other); 
    84         genMerge(other, ds.ways, mergeds.ways, wayshash); 
     140        mergePrimitive(other, myDataSet.ways, theirDataSet.ways, wayshash); 
    85141    } 
    86142 
    87143    public void visit(Relation other) { 
    88144        fixRelation(other); 
    89         genMerge(other, ds.relations, mergeds.relations, relshash); 
     145        mergePrimitive(other, myDataSet.relations, theirDataSet.relations, relshash); 
    90146    } 
    91147 
     
    95151     */ 
    96152    public void fixReferences() { 
    97         for (Way w : ds.ways) fixWay(w); 
    98         for (Relation r : ds.relations) fixRelation(r); 
     153        for (Way w : myDataSet.ways) { 
     154            fixWay(w); 
     155        } 
     156        for (Relation r : myDataSet.relations) { 
     157            fixRelation(r); 
     158        } 
    99159        for (OsmPrimitive osm : conflicts.values()) 
    100             if (osm instanceof Way) 
     160            if (osm instanceof Way) { 
    101161                fixWay((Way)osm); 
    102             else if (osm instanceof Relation) 
     162            } else if (osm instanceof Relation) { 
    103163                fixRelation((Relation) osm); 
     164            } 
    104165    } 
    105166 
     
    110171            Node otherN = (Node) merged.get(n); 
    111172            newNodes.add(otherN == null ? n : otherN); 
    112             if (otherN != null) 
     173            if (otherN != null) { 
    113174                replacedSomething = true; 
     175            } 
    114176        } 
    115177        if (replacedSomething) { 
     
    139201    } 
    140202 
    141     private static <P extends OsmPrimitive> boolean match(P p1, P p2) { 
    142         if ((p1.id == 0 || p2.id == 0) && !p1.incomplete && !p2.incomplete) { 
    143             return realMatch(p1, p2); 
    144         } 
    145         return p1.id == p2.id; 
    146     } 
    147  
    148     /** @return true if the prims have pretty much the same data, i.e. the 
    149      * same position, the same members, ... 
    150      */ 
    151     // Java cannot dispatch on generics... 
    152     private static boolean realMatch(OsmPrimitive p1, OsmPrimitive p2) { 
    153         if (p1 instanceof Node && p2 instanceof Node) { 
    154             return realMatch((Node) p1, (Node) p2); 
    155         } else if (p1 instanceof Way && p2 instanceof Way) { 
    156             return realMatch((Way) p1, (Way) p2); 
    157         } else if (p1 instanceof Relation && p2 instanceof Relation) { 
    158             return realMatch((Relation) p1, (Relation) p2); 
    159         } else { 
    160             throw new RuntimeException("arguments have unknown type"); 
    161         } 
    162     } 
    163  
    164     private static boolean realMatch(Node n1, Node n2) { 
    165         return n1.getCoor().equalsEpsilon(n2.getCoor()); 
    166     } 
    167  
    168     private static boolean realMatch(Way w1, Way w2) { 
    169         if (w1.nodes.size() != w2.nodes.size()) 
    170             return false; 
    171         Iterator<Node> it = w1.nodes.iterator(); 
    172         for (Node n : w2.nodes) 
    173             if (!match(n, it.next())) 
    174                 return false; 
    175         return true; 
    176     } 
    177  
    178     private static boolean realMatch(Relation w1, Relation w2) { 
    179         // FIXME this is not perfect yet... 
    180         if (w1.members.size() != w2.members.size()) 
    181             return false; 
    182         for (RelationMember em : w1.members) { 
    183             if (!w2.members.contains(em)) { 
    184                 return false; 
    185             } 
    186         } 
    187         return true; 
    188     } 
    189  
    190     /** 
    191      * Merge the common parts of an osm primitive. 
    192      * @param my The object, the information gets merged into 
    193      * @param other The object, the information gets merged from 
    194      */ 
    195     private void mergeCommon(OsmPrimitive my, OsmPrimitive other) { 
    196         if (other.deleted) 
    197             my.delete(true); 
    198         if (my.id == 0 || !my.modified || other.modified) { 
    199             if (my.id == 0 && other.id != 0) { 
    200                 my.id = other.id; 
    201                 my.modified = other.modified; // match a new node 
    202                 my.version = other.version; 
    203             } else if (my.id != 0 && other.id != 0 && other.modified) 
    204                 my.modified = true; 
    205         } 
    206         if (other.keys == null) 
    207             return; 
    208         if (my.keySet().containsAll(other.keys.keySet())) 
    209             return; 
    210         if (my.keys == null) 
    211             my.keys = other.keys; 
    212         else 
    213             my.keys.putAll(other.keys); 
    214  
    215         my.modified = true; 
    216     } 
    217  
    218203    /** 
    219204     * Tries to merge a primitive <code>other</code> into an existing primitive with the same id. 
    220205     * 
    221206     * @param myPrimitives the complete set of my primitives (potential merge targets) 
    222      * @param myPrimitivesWithID the map of primitives (potential merge targets) with an id <> 0, for faster lookup 
     207     * @param myPrimitivesWithDefinedIds the map of primitives (potential merge targets) with an id <> 0, for faster lookup 
    223208     *    by id. Key is the id, value the primitive with the given value. myPrimitives.valueSet() is a 
    224209     *    subset of primitives. 
    225      * @param other  the other primitive which is to be merged with a primitive in primitives if possible 
     210     * @param other  the other primitive which is to be merged onto a primitive in my primitives 
    226211     * @return true, if this method was able to merge <code>other</code> with an existing node; false, otherwise 
    227212     */ 
    228213    private <P extends OsmPrimitive> boolean mergeById( 
    229             Collection<P> myPrimitives, HashMap<Long, P> myPrimitivesWithID, P other) { 
     214            Collection<P> myPrimitives, HashMap<Long, P> myPrimitivesWithDefinedIds, P other) { 
    230215 
    231216        // merge other into an existing primitive with the same id, if possible 
    232217        // 
    233         if (myPrimitivesWithID.containsKey(other.id)) { 
    234             P my = myPrimitivesWithID.get(other.id); 
    235             if (my.realEqual(other, true /* compare semantic fields only */)) { 
    236                 // make sure the merge target becomes the higher version number 
    237                 // and the later timestamp 
    238                 // 
    239                 my.version = Math.max(other.version, my.version); 
    240                 if (other.getTimestamp().after(my.getTimestamp())) { 
    241                     my.setTimestamp(other.getTimestamp()); 
     218        if (myPrimitivesWithDefinedIds.containsKey(other.id)) { 
     219            P my = myPrimitivesWithDefinedIds.get(other.id); 
     220            if (my.version <= other.version) { 
     221                if (! my.visible && other.visible) { 
     222                    // should not happen 
     223                    // 
     224                    logger.warning(tr("My primitive with id {0} and version {1} is visible although " 
     225                            + "their primitive with lower version {2} is not visible. " 
     226                            + "Can't deal with this inconsistency. Keeping my primitive. ", 
     227                            Long.toString(my.id),Long.toString(my.version), Long.toString(other.version) 
     228                    )); 
     229                    merged.put(other, my); 
     230                } else if (my.visible && ! other.visible) { 
     231                    // this is always a conflict because the user has to decide whether 
     232                    // he wants to create a clone of its local primitive or whether he 
     233                    // wants to purge my from the local dataset. He can't keep it unchanged 
     234                    // because it was deleted on the server. 
     235                    // 
     236                    conflicts.put(my,other); 
     237                } else if (! my.modified && other.modified) { 
     238                    // my not modified. We can assume that other is the most recent version. 
     239                    // clone it onto my. But check first, whether other is deleted. if so, 
     240                    // make sure that my is not references anymore in myDataSet. 
     241                    // 
     242                    if (other.deleted) { 
     243                        myDataSet.unlinkReferencesToPrimitive(my); 
     244                    } 
     245                    my.cloneFrom(other); 
     246                    merged.put(other, my); 
     247                } else if (! my.modified && !other.modified) { 
     248                    // nothing to merge 
     249                    // 
     250                    merged.put(other,my); 
     251                } else if (my.deleted != other.deleted) { 
     252                    // if we get here my is modified. Differences in deleted state 
     253                    // have to be resolved manually 
     254                    // 
     255                    conflicts.put(my,other); 
     256                } else if (! my.hasEqualSemanticAttributes(other)) { 
     257                    // my is modified and is not semantically equal with other. Can't automatically 
     258                    // resolve the differences 
     259                    // =>  create a conflict 
     260                    conflicts.put(my,other); 
     261                } else { 
     262                    // clone from other, but keep the modified flag. Clone will mainly copy 
     263                    // technical attributes like timestamp or user information. Semantic 
     264                    // attributes should already be equal if we get here. 
     265                    // 
     266                    my.cloneFrom(other); 
     267                    my.modified = true; 
     268                    merged.put(other, my); 
    242269                } 
     270            } else { 
     271                // my.version > other.version => keep my version 
    243272                merged.put(other, my); 
    244                 return true; 
    245             } 
    246         } 
    247  
    248         // try to merge into one of the existing primitives 
    249         // 
    250         for (P my : myPrimitives) { 
    251             if (my.realEqual(other, false /* compare all fields */)) { 
    252                 merged.put(other, my); 
    253                 return true; // no merge needed. 
    254             } 
    255             if (my.realEqual(other, true)) { 
    256                 // they differ in modified/version combination only. Auto-resolve it. 
    257                 merged.put(other, my); 
    258                 if (my.version < other.version) { 
    259                     my.version = other.version; 
    260                     my.modified = other.modified; 
    261                     my.setTimestamp(other.getTimestamp()); 
    262                 } 
    263                 return true; // merge done. 
    264             } 
    265             if (my.id == other.id && my.id != 0) { 
    266                 if (my.incomplete || other.incomplete) { 
    267                     if (my.incomplete) { 
    268                         my.cloneFrom(other); 
    269                     } 
    270                 } else if (my.modified && other.modified) { 
    271                     conflicts.put(my, other); 
    272                 } else if (!my.modified && !other.modified) { 
    273                     if (my.version < other.version) { 
    274                         my.cloneFrom(other); 
    275                     } 
    276                 } else if (other.modified) { 
    277                     if (my.version > other.version) { 
    278                         conflicts.put(my, other); 
    279                     } else { 
    280                         my.cloneFrom(other); 
    281                     } 
    282                 } else if (my.modified) { 
    283                     if (my.version < other.version) { 
    284                         conflicts.put(my, other); 
    285                     } 
    286                 } 
    287                 merged.put(other, my); 
    288                 return true; 
    289             } 
     273            } 
     274            return true; 
    290275        } 
    291276        return false; 
    292277    } 
     278 
     279 
     280    /** 
     281     * Runs the merge operation. Successfully merged {@see OsmPrimitive}s are in 
     282     * {@see #getMyDataSet()}. 
     283     *  
     284     * See {@see #getConflicts()} for a map of conflicts after the merge operation. 
     285     */ 
     286    public void merge() { 
     287        for (final OsmPrimitive primitive : theirDataSet.allPrimitives()) { 
     288            primitive.visit(this); 
     289        } 
     290        fixReferences(); 
     291    } 
     292 
     293    /** 
     294     * replies my dataset 
     295     *  
     296     * @return 
     297     */ 
     298    public DataSet getMyDataSet() { 
     299        return myDataSet; 
     300    } 
     301 
     302 
     303    /** 
     304     * replies the map of conflicts 
     305     *  
     306     * @return the map of conflicts 
     307     */ 
     308    public Map<OsmPrimitive, OsmPrimitive> getConflicts() { 
     309        return conflicts; 
     310    } 
    293311} 
  • trunk/src/org/openstreetmap/josm/gui/conflict/ConflictResolver.java

    r1654 r1690  
    2323import org.openstreetmap.josm.gui.conflict.nodes.NodeListMergeModel; 
    2424import org.openstreetmap.josm.gui.conflict.nodes.NodeListMerger; 
     25import org.openstreetmap.josm.gui.conflict.properties.OperationCancelledException; 
    2526import org.openstreetmap.josm.gui.conflict.properties.PropertiesMergeModel; 
    2627import org.openstreetmap.josm.gui.conflict.properties.PropertiesMerger; 
     
    145146        this.their =  their; 
    146147        propertiesMerger.getModel().populate(my, their); 
     148        if (propertiesMerger.getModel().hasVisibleStateConflict()) { 
     149            tabbedPane.setEnabledAt(1, false); 
     150            tabbedPane.setEnabledAt(2, false); 
     151            tabbedPane.setEnabledAt(3, false); 
     152            return; 
     153        } 
    147154        tabbedPane.setEnabledAt(0, true); 
    148155        tagMerger.getModel().populate(my, their); 
     
    165172            tabbedPane.setEnabledAt(3, true); 
    166173        } 
     174 
    167175    } 
    168176 
     
    173181     * @return the resolution command 
    174182     */ 
    175     public Command buildResolveCommand() { 
     183    public Command buildResolveCommand() throws OperationCancelledException { 
    176184        ArrayList<Command> commands = new ArrayList<Command>(); 
    177         if (tagMerger.getModel().getNumResolvedConflicts() > 0) { 
    178             commands.add(tagMerger.getModel().buildResolveCommand(my, their)); 
    179         } 
    180         commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their)); 
    181         if (my instanceof Way && nodeListMerger.getModel().isFrozen()) { 
    182             NodeListMergeModel model  =(NodeListMergeModel)nodeListMerger.getModel(); 
    183             commands.add(model.buildResolveCommand((Way)my, (Way)their)); 
    184         } else if (my instanceof Relation && relationMemberMerger.getModel().isFrozen()) { 
    185             RelationMemberListMergeModel model  =(RelationMemberListMergeModel)relationMemberMerger.getModel(); 
    186             commands.add(model.buildResolveCommand((Relation)my, (Relation)their)); 
    187         } 
    188         if (isResolvedCompletely()) { 
    189             commands.add( 
    190                     new VersionConflictResolveCommand(my, their) 
    191             ); 
     185        if (propertiesMerger.getModel().hasVisibleStateConflict()) { 
     186            if (propertiesMerger.getModel().isDecidedVisibleState()) { 
     187                commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their)); 
     188            } 
     189        } else { 
     190            if (tagMerger.getModel().getNumResolvedConflicts() > 0) { 
     191                commands.add(tagMerger.getModel().buildResolveCommand(my, their)); 
     192            } 
     193            commands.addAll(propertiesMerger.getModel().buildResolveCommand(my, their)); 
     194            if (my instanceof Way && nodeListMerger.getModel().isFrozen()) { 
     195                NodeListMergeModel model  =(NodeListMergeModel)nodeListMerger.getModel(); 
     196                commands.add(model.buildResolveCommand((Way)my, (Way)their)); 
     197            } else if (my instanceof Relation && relationMemberMerger.getModel().isFrozen()) { 
     198                RelationMemberListMergeModel model  =(RelationMemberListMergeModel)relationMemberMerger.getModel(); 
     199                commands.add(model.buildResolveCommand((Relation)my, (Relation)their)); 
     200            } 
     201            if (isResolvedCompletely()) { 
     202                commands.add( 
     203                        new VersionConflictResolveCommand(my, their) 
     204                ); 
     205            } 
    192206        } 
    193207        return new SequenceCommand(tr("Conflict Resolution"), commands); 
  • trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMergeModel.java

    r1669 r1690  
    33 
    44import static org.openstreetmap.josm.gui.conflict.MergeDecisionType.UNDECIDED; 
     5import static org.openstreetmap.josm.tools.I18n.tr; 
    56 
    67import java.beans.PropertyChangeListener; 
    78import java.beans.PropertyChangeSupport; 
     9import java.io.IOException; 
     10import java.net.HttpURLConnection; 
    811import java.util.ArrayList; 
     12import java.util.HashMap; 
    913import java.util.List; 
    1014import java.util.Observable; 
    1115 
     16import javax.swing.JOptionPane; 
     17import javax.swing.text.html.HTML; 
     18 
     19import org.openstreetmap.josm.Main; 
    1220import org.openstreetmap.josm.command.Command; 
    1321import org.openstreetmap.josm.command.CoordinateConflictResolveCommand; 
    1422import org.openstreetmap.josm.command.DeletedStateConflictResolveCommand; 
     23import org.openstreetmap.josm.command.PurgePrimitivesCommand; 
     24import org.openstreetmap.josm.command.UndeletePrimitivesCommand; 
    1525import org.openstreetmap.josm.data.coor.LatLon; 
     26import org.openstreetmap.josm.data.osm.DataSet; 
    1627import org.openstreetmap.josm.data.osm.Node; 
    1728import org.openstreetmap.josm.data.osm.OsmPrimitive; 
     29import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 
     30import org.openstreetmap.josm.data.osm.Relation; 
     31import org.openstreetmap.josm.data.osm.RelationMember; 
     32import org.openstreetmap.josm.data.osm.Way; 
     33import org.openstreetmap.josm.gui.PleaseWaitRunnable; 
    1834import org.openstreetmap.josm.gui.conflict.MergeDecisionType; 
     35import org.openstreetmap.josm.gui.conflict.properties.PropertiesMerger.KeepMyVisibleStateAction; 
     36import org.openstreetmap.josm.io.MultiFetchServerObjectReader; 
     37import org.openstreetmap.josm.io.OsmApi; 
     38import org.openstreetmap.josm.io.OsmApiException; 
     39import org.openstreetmap.josm.io.OsmServerObjectReader; 
     40import org.openstreetmap.josm.io.OsmTransferException; 
     41import org.xml.sax.SAXException; 
    1942 
    2043/** 
    2144 * This is the model for resolving conflicts in the properties of the 
    2245 * {@see OsmPrimitive}s. In particular, it represents conflicts in the coordiates of {@see Node}s and 
    23  * the deleted state of {@see OsmPrimitive}s. 
     46 * the deleted or visible state of {@see OsmPrimitive}s. 
    2447 * 
    2548 * This model is an {@see Observable}. It notifies registered {@see Observer}s whenever the 
     
    3154 * @see Node#getCoor() 
    3255 * @see OsmPrimitive#deleted 
     56 * @see OsmPrimitive#visible 
    3357 * 
    3458 */ 
     
    3761    static public final String RESOLVED_COMPLETELY_PROP = PropertiesMergeModel.class.getName() + ".resolvedCompletely"; 
    3862 
     63    private OsmPrimitive my; 
     64 
    3965    private LatLon myCoords; 
    4066    private LatLon theirCoords; 
     
    4369    private boolean myDeletedState; 
    4470    private boolean theirDeletedState; 
     71    private boolean myVisibleState; 
     72    private boolean theirVisibleState; 
    4573    private MergeDecisionType deletedMergeDecision; 
     74    private MergeDecisionType visibleMergeDecision; 
    4675    private final PropertyChangeSupport support; 
    4776    private boolean resolvedCompletely; 
     
    91120 
    92121    /** 
     122     * replies true if there is a  conflict in the visible state and if this conflict is 
     123     * resolved 
     124     * 
     125     * @return true if there is a conflict in the visible state and if this conflict is 
     126     * resolved; false, otherwise 
     127     */ 
     128    public boolean isDecidedVisibleState() { 
     129        return ! visibleMergeDecision.equals(UNDECIDED); 
     130    } 
     131 
     132    /** 
    93133     * replies true if the current decision for the coordinate conflict is <code>decision</code> 
    94134     * 
     
    111151 
    112152    /** 
     153     * replies true if the current decision for the visible state conflict is <code>decision</code> 
     154     * 
     155     * @return true if the current decision for the visible state conflict is <code>decision</code>; 
     156     *  false, otherwise 
     157     */ 
     158    public boolean isVisibleStateDecision(MergeDecisionType decision) { 
     159        return visibleMergeDecision.equals(decision); 
     160    } 
     161    /** 
    113162     * populates the model with the differences between my and their version 
    114163     * 
     
    117166     */ 
    118167    public void populate(OsmPrimitive my, OsmPrimitive their) { 
     168        this.my = my; 
    119169        if (my instanceof Node) { 
    120170            myCoords = ((Node)my).getCoor(); 
     
    128178        theirDeletedState = their.deleted; 
    129179 
     180        myVisibleState = my.visible; 
     181        theirVisibleState = their.visible; 
     182 
    130183        coordMergeDecision = UNDECIDED; 
    131184        deletedMergeDecision = UNDECIDED; 
     185        visibleMergeDecision = UNDECIDED; 
    132186        setChanged(); 
    133187        notifyObservers(); 
     
    209263    } 
    210264 
    211     public void decideDeletedStateConflict(MergeDecisionType decision) { 
     265 
     266    /** 
     267     * replies my visible state, 
     268     * @return my visible state 
     269     */ 
     270    public Boolean getMyVisibleState() { 
     271        return myVisibleState; 
     272    } 
     273 
     274    /** 
     275     * replies their visible state, 
     276     * @return their visible state 
     277     */ 
     278    public  Boolean getTheirVisibleState() { 
     279        return theirVisibleState; 
     280    } 
     281 
     282    /** 
     283     * replies the merged visible state; null, if the merge decision is 
     284     * {@see MergeDecisionType#UNDECIDED}. 
     285     *  
     286     * @return the merged visible state 
     287     */ 
     288    public Boolean getMergedVisibleState() { 
     289        switch(visibleMergeDecision) { 
     290        case KEEP_MINE: return myVisibleState; 
     291        case KEEP_THEIR: return theirVisibleState; 
     292        case UNDECIDED: return null; 
     293        } 
     294        // should not happen 
     295        return null; 
     296    } 
     297 
     298    /** 
     299     * decides the conflict between two deleted states 
     300     * @param decision the decision (must not be null) 
     301     *  
     302     * @throws IllegalArgumentException thrown, if decision is null 
     303     */ 
     304    public void decideDeletedStateConflict(MergeDecisionType decision) throws IllegalArgumentException{ 
     305        if (decision == null) 
     306            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "decision")); 
    212307        this.deletedMergeDecision = decision; 
     308        setChanged(); 
     309        notifyObservers(); 
     310        fireCompletelyResolved(); 
     311    } 
     312 
     313    /** 
     314     * decides the conflict between two visible states 
     315     * @param decision the decision (must not be null) 
     316     *  
     317     * @throws IllegalArgumentException thrown, if decision is null 
     318     */ 
     319    public void decideVisibleStateConflict(MergeDecisionType decision) throws IllegalArgumentException { 
     320        if (decision == null) 
     321            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "decision")); 
     322        this.visibleMergeDecision = decision; 
    213323        setChanged(); 
    214324        notifyObservers(); 
     
    242352 
    243353    /** 
     354     * replies true if my and their primitive have a conflict between 
     355     * their visible states 
     356     * 
     357     * @return true if my and their primitive have a conflict between 
     358     * their visible states 
     359     */ 
     360    public boolean hasVisibleStateConflict() { 
     361        return myVisibleState != theirVisibleState; 
     362    } 
     363 
     364    /** 
    244365     * replies true if all conflict in this model are resolved 
    245366     * 
     
    254375            ret = ret && ! deletedMergeDecision.equals(UNDECIDED); 
    255376        } 
     377        if (hasVisibleStateConflict()) { 
     378            ret = ret && ! visibleMergeDecision.equals(UNDECIDED); 
     379        } 
    256380        return ret; 
    257381    } 
     
    264388     * @return the list of commands 
    265389     */ 
    266     public List<Command> buildResolveCommand(OsmPrimitive my, OsmPrimitive their) { 
     390    public List<Command> buildResolveCommand(OsmPrimitive my, OsmPrimitive their) throws OperationCancelledException{ 
    267391        ArrayList<Command> cmds = new ArrayList<Command>(); 
     392        if (hasVisibleStateConflict() && isDecidedVisibleState()) { 
     393            if (isVisibleStateDecision(MergeDecisionType.KEEP_MINE)) { 
     394                try { 
     395                    UndeletePrimitivesCommand cmd = createUndeletePrimitiveCommand(my); 
     396                    if (cmd == null) 
     397                        throw new OperationCancelledException(); 
     398                    cmds.add(cmd); 
     399                } catch(OsmTransferException e) { 
     400                    handleExceptionWhileBuildingCommand(e); 
     401                    throw new OperationCancelledException(e); 
     402                } 
     403            } else if (isVisibleStateDecision(MergeDecisionType.KEEP_THEIR)) { 
     404                cmds.add(new PurgePrimitivesCommand(my)); 
     405            } 
     406        } 
    268407        if (hasCoordConflict() && isDecidedCoord()) { 
    269408            cmds.add(new CoordinateConflictResolveCommand((Node)my, (Node)their, coordMergeDecision)); 
     
    274413        return cmds; 
    275414    } 
     415 
     416    public OsmPrimitive getMyPrimitive() { 
     417        return my; 
     418    } 
     419 
     420    /** 
     421     *  
     422     * @param id 
     423     */ 
     424    protected void handleExceptionWhileBuildingCommand(Exception e) { 
     425        e.printStackTrace(); 
     426        String msg = e.getMessage() != null ? e.getMessage() : e.toString(); 
     427        msg = msg.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"); 
     428        JOptionPane.showMessageDialog( 
     429                Main.parent, 
     430                tr("<html>An error occurred while communicating with the server<br>" 
     431                        + "Details: {0}</html>", 
     432                        msg 
     433                ), 
     434                tr("Communication with server failed"), 
     435                JOptionPane.ERROR_MESSAGE 
     436        ); 
     437    } 
     438 
     439    /** 
     440     * User has decided to keep his local version of a primitive which had been deleted 
     441     * on the server 
     442     *  
     443     * @param id the primitive id 
     444     */ 
     445    protected UndeletePrimitivesCommand createUndeletePrimitiveCommand(OsmPrimitive my) throws OsmTransferException { 
     446        if (my instanceof Node) 
     447            return createUndeleteNodeCommand((Node)my); 
     448        else if (my instanceof Way) 
     449            return createUndeleteWayCommand((Way)my); 
     450        else if (my instanceof Relation) 
     451            return createUndeleteRelationCommand((Relation)my); 
     452        return null; 
     453    } 
     454    /** 
     455     * Undelete a node which is already deleted on the server. The API 
     456     * doesn't offer a call for "undeleting" a node. We therefore create 
     457     * a clone of the node which we flag as new. On the next upload the 
     458     * server will assign the node a new id. 
     459     *  
     460     * @param node the node to undelete 
     461     */ 
     462    protected UndeletePrimitivesCommand  createUndeleteNodeCommand(Node node) { 
     463        return new UndeletePrimitivesCommand(node); 
     464    } 
     465 
     466    /** 
     467     * displays a confirmation message. The user has to confirm that additional dependent 
     468     * nodes should be undeleted too. 
     469     *  
     470     * @param way  the way 
     471     * @param dependent a list of dependent nodes which have to be undelete too 
     472     * @return true, if the user confirms; false, otherwise 
     473     */ 
     474    protected boolean confirmUndeleteDependentPrimitives(Way way, ArrayList<OsmPrimitive> dependent) { 
     475        String [] options = { 
     476                tr("Yes, undelete them too"), 
     477                tr("No, cancel operation") 
     478        }; 
     479        int ret = JOptionPane.showOptionDialog( 
     480                Main.parent, 
     481                tr("<html>There are {0} additional nodes used by way {1}<br>" 
     482                        + "which are deleted on the server.<br>" 
     483                        + "<br>" 
     484                        + "Do you want to undelete these nodes too?</html>", 
     485                        Long.toString(dependent.size()), Long.toString(way.id)), 
     486                        tr("Undelete additional nodes?"), 
     487                        JOptionPane.YES_NO_OPTION, 
     488                        JOptionPane.QUESTION_MESSAGE, 
     489                        null, 
     490                        options, 
     491                        options[0] 
     492        ); 
     493 
     494        switch(ret) { 
     495        case JOptionPane.CLOSED_OPTION: return false; 
     496        case JOptionPane.YES_OPTION: return true; 
     497        case JOptionPane.NO_OPTION: return false; 
     498        } 
     499        return false; 
     500 
     501    } 
     502 
     503    protected boolean confirmUndeleteDependentPrimitives(Relation r, ArrayList<OsmPrimitive> dependent) { 
     504        String [] options = { 
     505                tr("Yes, undelete them too"), 
     506                tr("No, cancel operation") 
     507        }; 
     508        int ret = JOptionPane.showOptionDialog( 
     509                Main.parent, 
     510                tr("<html>There are {0} additional primitives referred to by relation {1}<br>" 
     511                        + "which are deleted on the server.<br>" 
     512                        + "<br>" 
     513                        + "Do you want to undelete them too?</html>", 
     514                        Long.toString(dependent.size()), Long.toString(r.id)), 
     515                        tr("Undelete dependent primitives?"), 
     516                        JOptionPane.YES_NO_OPTION, 
     517                        JOptionPane.QUESTION_MESSAGE, 
     518                        null, 
     519                        options, 
     520                        options[0] 
     521        ); 
     522 
     523        switch(ret) { 
     524        case JOptionPane.CLOSED_OPTION: return false; 
     525        case JOptionPane.YES_OPTION: return true; 
     526        case JOptionPane.NO_OPTION: return false; 
     527        } 
     528        return false; 
     529 
     530    } 
     531 
     532    /** 
     533     * Creates the undelete command for a way which is already deleted on the server. 
     534     *  
     535     * This method also checks whether there are additional nodes referred to by 
     536     * this way which are deleted on the server too. 
     537     *  
     538     * @param way the way to undelete 
     539     * @return the undelete command 
     540     * @see #createUndeleteNodeCommand(Node) 
     541     */ 
     542    protected UndeletePrimitivesCommand createUndeleteWayCommand(final Way way) throws OsmTransferException { 
     543 
     544        HashMap<Long,OsmPrimitive> candidates = new HashMap<Long,OsmPrimitive>(); 
     545        for (Node n : way.nodes) { 
     546            if (n.id > 0 && ! candidates.values().contains(n)) { 
     547                candidates.put(n.id, n); 
     548            } 
     549        } 
     550        MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader(); 
     551        reader.append(candidates.values()); 
     552        DataSet ds = reader.parseOsm(); 
     553 
     554        ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>(); 
     555        for (OsmPrimitive their : ds.allPrimitives()) { 
     556            if (candidates.keySet().contains(their.id) && ! their.visible) { 
     557                toDelete.add(candidates.get(their.id)); 
     558            } 
     559        } 
     560        if (!toDelete.isEmpty()) { 
     561            if (! confirmUndeleteDependentPrimitives(way, toDelete)) 
     562                // FIXME: throw exception ? 
     563                return null; 
     564        } 
     565        toDelete.add(way); 
     566        return new UndeletePrimitivesCommand(toDelete); 
     567    } 
     568 
     569    /** 
     570     * Creates an undelete command for a relation which is already deleted on the server. 
     571     *  
     572     * This method  checks whether there are additional primitives referred to by 
     573     * this relation which are already deleted on the server. 
     574     *  
     575     * @param r the relation 
     576     * @return the undelete command 
     577     * @see #createUndeleteNodeCommand(Node) 
     578     */ 
     579    protected UndeletePrimitivesCommand createUndeleteRelationCommand(final Relation r) throws OsmTransferException { 
     580 
     581        HashMap<Long,OsmPrimitive> candidates = new HashMap<Long, OsmPrimitive>(); 
     582        for (RelationMember m : r.members) { 
     583            if (m.member.id > 0 && !candidates.values().contains(m.member)) { 
     584                candidates.put(m.member.id,m.member); 
     585            } 
     586        } 
     587 
     588        MultiFetchServerObjectReader reader = new MultiFetchServerObjectReader(); 
     589        reader.append(candidates.values()); 
     590        DataSet ds = reader.parseOsm(); 
     591 
     592        ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>(); 
     593        for (OsmPrimitive their : ds.allPrimitives()) { 
     594            if (candidates.keySet().contains(their.id) && ! their.visible) { 
     595                toDelete.add(candidates.get(their.id)); 
     596            } 
     597        } 
     598        if (!toDelete.isEmpty()) { 
     599            if (! confirmUndeleteDependentPrimitives(r, toDelete)) 
     600                // FIXME: throw exception ? 
     601                return null; 
     602        } 
     603        toDelete.add(r); 
     604        return new UndeletePrimitivesCommand(toDelete); 
     605    } 
     606 
    276607} 
  • trunk/src/org/openstreetmap/josm/gui/conflict/properties/PropertiesMerger.java

    r1676 r1690  
    1818import javax.swing.JButton; 
    1919import javax.swing.JLabel; 
     20import javax.swing.JOptionPane; 
    2021import javax.swing.JPanel; 
    21  
     22import javax.swing.SwingUtilities; 
     23 
     24import org.openstreetmap.josm.Main; 
    2225import org.openstreetmap.josm.data.coor.LatLon; 
     26import org.openstreetmap.josm.data.osm.OsmPrimitiveType; 
    2327import org.openstreetmap.josm.gui.conflict.MergeDecisionType; 
    2428import org.openstreetmap.josm.tools.ImageProvider; 
     
    4852    private JLabel lblTheirDeletedState; 
    4953 
     54    private JLabel lblMyVisibleState; 
     55    private JLabel lblMergedVisibleState; 
     56    private JLabel lblTheirVisibleState; 
     57 
    5058    private final PropertiesMergeModel model; 
    5159 
     
    5967    } 
    6068 
    61     protected void build() { 
    62         setLayout(new GridBagLayout()); 
     69    protected void buildHeaderRow() { 
    6370        GridBagConstraints gc = new GridBagConstraints(); 
    6471 
    65         // ------------------ 
    6672        gc.gridx = 1; 
    6773        gc.gridy = 0; 
     
    8894        lblTheirVersion.setToolTipText(tr("Properties in their dataset, i.e. the server dataset")); 
    8995        add(lblTheirVersion, gc); 
    90  
    91         // -------------------------------- 
     96    } 
     97 
     98    protected void buildCoordinateConflictRows() { 
     99        GridBagConstraints gc = new GridBagConstraints(); 
     100 
    92101        gc.gridx = 0; 
    93102        gc.gridy = 1; 
     
    161170        JButton btnUndecideCoordinates = new JButton(actUndecideCoordinates); 
    162171        add(btnUndecideCoordinates, gc); 
    163         // --------------------------------------------------- 
     172    } 
     173 
     174    protected void buildDeletedStateConflictRows() { 
     175        GridBagConstraints gc = new GridBagConstraints(); 
    164176 
    165177        gc.gridx = 0; 
     
    191203        model.addObserver(actKeepMyDeletedState); 
    192204        JButton btnKeepMyDeletedState = new JButton(actKeepMyDeletedState); 
    193         btnKeepMyCoordinates.setName("button.keepmydeletedstate"); 
     205        btnKeepMyDeletedState.setName("button.keepmydeletedstate"); 
    194206        add(btnKeepMyDeletedState, gc); 
    195207 
     
    212224        model.addObserver(actKeepTheirDeletedState); 
    213225        JButton btnKeepTheirDeletedState = new JButton(actKeepTheirDeletedState); 
    214         btnKeepMyCoordinates.setName("button.keeptheirdeletedstate"); 
     226        btnKeepTheirDeletedState.setName("button.keeptheirdeletedstate"); 
    215227        add(btnKeepTheirDeletedState, gc); 
    216228 
     
    233245        model.addObserver(actUndecideDeletedState); 
    234246        JButton btnUndecideDeletedState = new JButton(actUndecideDeletedState); 
    235         btnKeepMyCoordinates.setName("button.undecidedeletedstate"); 
     247        btnUndecideDeletedState.setName("button.undecidedeletedstate"); 
    236248        add(btnUndecideDeletedState, gc); 
    237  
     249    } 
     250 
     251    protected void buildVisibleStateRows() { 
     252        GridBagConstraints gc = new GridBagConstraints(); 
     253 
     254        gc.gridx = 0; 
     255        gc.gridy = 5; 
     256        gc.gridwidth = 1; 
     257        gc.gridheight = 1; 
     258        gc.fill = GridBagConstraints.BOTH; 
     259        gc.anchor = GridBagConstraints.LINE_START; 
     260        gc.weightx = 0.0; 
     261        gc.weighty = 0.0; 
     262        gc.insets = new Insets(0,5,0,5); 
     263        add(new JLabel(tr("Visible State:")), gc); 
     264 
     265        gc.gridx = 1; 
     266        gc.gridy = 5; 
     267        gc.fill = GridBagConstraints.BOTH; 
     268        gc.anchor = GridBagConstraints.CENTER; 
     269        gc.weightx = 0.33; 
     270        gc.weighty = 0.0; 
     271        add(lblMyVisibleState = buildValueLabel("label.myvisiblestate"), gc); 
     272 
     273        gc.gridx = 2; 
     274        gc.gridy = 5; 
     275        gc.fill = GridBagConstraints.NONE; 
     276        gc.anchor = GridBagConstraints.CENTER; 
     277        gc.weightx = 0.0; 
     278        gc.weighty = 0.0; 
     279        KeepMyVisibleStateAction actKeepMyVisibleState = new KeepMyVisibleStateAction(); 
     280        model.addObserver(actKeepMyVisibleState); 
     281        JButton btnKeepMyVisibleState = new JButton(actKeepMyVisibleState); 
     282        btnKeepMyVisibleState.setName("button.keepmyvisiblestate"); 
     283        add(btnKeepMyVisibleState, gc); 
     284 
     285        gc.gridx = 3; 
     286        gc.gridy = 5; 
     287        gc.fill = GridBagConstraints.BOTH; 
     288        gc.anchor = GridBagConstraints.CENTER; 
     289        gc.weightx = 0.33; 
     290        gc.weighty = 0.0; 
     291        add(lblMergedVisibleState = buildValueLabel("label.mergedvisiblestate"), gc); 
     292 
     293        gc.gridx = 4; 
     294        gc.gridy = 5; 
     295        gc.fill = GridBagConstraints.NONE; 
     296        gc.anchor = GridBagConstraints.CENTER; 
     297        gc.weightx = 0.0; 
     298        gc.weighty = 0.0; 
     299        KeepTheirVisibleStateAction actKeepTheirVisibleState = new KeepTheirVisibleStateAction(); 
     300        model.addObserver(actKeepTheirVisibleState); 
     301        JButton btnKeepTheirVisibleState = new JButton(actKeepTheirVisibleState); 
     302        btnKeepTheirVisibleState.setName("button.keeptheirvisiblestate"); 
     303        add(btnKeepTheirVisibleState, gc); 
     304 
     305        gc.gridx = 5; 
     306        gc.gridy = 5; 
     307        gc.fill = GridBagConstraints.BOTH; 
     308        gc.anchor = GridBagConstraints.CENTER; 
     309        gc.weightx = 0.33; 
     310        gc.weighty = 0.0; 
     311        add(lblTheirVisibleState = buildValueLabel("label.theirvisiblestate"), gc); 
     312 
     313        // --------------------------------------------------- 
     314        gc.gridx = 3; 
     315        gc.gridy = 6; 
     316        gc.fill = GridBagConstraints.NONE; 
     317        gc.anchor = GridBagConstraints.CENTER; 
     318        gc.weightx = 0.0; 
     319        gc.weighty = 0.0; 
     320        UndecideVisibleStateConflictAction actUndecideVisibleState = new UndecideVisibleStateConflictAction(); 
     321        model.addObserver(actUndecideVisibleState); 
     322        JButton btnUndecideVisibleState = new JButton(actUndecideVisibleState); 
     323        btnUndecideVisibleState.setName("button.undecidevisiblestate"); 
     324        add(btnUndecideVisibleState, gc); 
     325    } 
     326 
     327    protected void build() { 
     328        setLayout(new GridBagLayout()); 
     329        buildHeaderRow(); 
     330        buildCoordinateConflictRows(); 
     331        buildDeletedStateConflictRows(); 
     332        buildVisibleStateRows(); 
    238333    } 
    239334 
     
    265360    } 
    266361 
    267     protected void updateCoordiates() { 
     362    public String visibleStateToString(Boolean visible) { 
     363        if (visible == null) 
     364            return tr("(none)"); 
     365        if (visible) 
     366            return tr("visible (on the server)"); 
     367        else 
     368            return tr("not visible (on the server)"); 
     369    } 
     370 
     371    public String visibleStateToStringMerged(Boolean visible) { 
     372        if (visible == null) 
     373            return tr("(none)"); 
     374        if (visible) 
     375            return tr("Keep a clone of the local version"); 
     376        else 
     377            return tr("Physically delete from local dataset"); 
     378    } 
     379 
     380    protected void updateCoordinates() { 
    268381        lblMyCoordinates.setText(coordToString(model.getMyCoords())); 
    269382        lblMergedCoordinates.setText(coordToString(model.getMergedCoords())); 
     
    320433    } 
    321434 
     435    protected void updateVisibleState() { 
     436        lblMyVisibleState.setText(visibleStateToString(model.getMyVisibleState())); 
     437        lblMergedVisibleState.setText(visibleStateToStringMerged(model.getMergedVisibleState())); 
     438        lblTheirVisibleState.setText(visibleStateToString(model.getTheirVisibleState())); 
     439 
     440        if (! model.hasVisibleStateConflict()) { 
     441            lblMyVisibleState.setBackground(BGCOLOR_NO_CONFLICT); 
     442            lblMergedVisibleState.setBackground(BGCOLOR_NO_CONFLICT); 
     443            lblTheirVisibleState.setBackground(BGCOLOR_NO_CONFLICT); 
     444        } else { 
     445            if (!model.isDecidedVisibleState()) { 
     446                lblMyVisibleState.setBackground(BGCOLOR_UNDECIDED); 
     447                lblMergedVisibleState.setBackground(BGCOLOR_NO_CONFLICT); 
     448                lblTheirVisibleState.setBackground(BGCOLOR_UNDECIDED); 
     449            } else { 
     450                lblMyVisibleState.setBackground( 
     451                        model.isVisibleStateDecision(MergeDecisionType.KEEP_MINE) 
     452                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT 
     453                ); 
     454                lblMergedVisibleState.setBackground(BGCOLOR_DECIDED); 
     455                lblTheirVisibleState.setBackground( 
     456                        model.isVisibleStateDecision(MergeDecisionType.KEEP_THEIR) 
     457                        ? BGCOLOR_DECIDED : BGCOLOR_NO_CONFLICT 
     458                ); 
     459            } 
     460        } 
     461    } 
     462 
    322463    public void update(Observable o, Object arg) { 
    323         updateCoordiates(); 
     464        updateCoordinates(); 
    324465        updateDeletedState(); 
     466        updateVisibleState(); 
    325467    } 
    326468 
     
    418560        } 
    419561    } 
     562 
     563    class KeepMyVisibleStateAction extends AbstractAction implements Observer { 
     564        public KeepMyVisibleStateAction() { 
     565            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeepmine")); 
     566            putValue(Action.SHORT_DESCRIPTION, tr("Keep my visible state")); 
     567        } 
     568 
     569        public void actionPerformed(ActionEvent e) { 
     570            if (confirmKeepMine()) { 
     571                model.decideVisibleStateConflict(MergeDecisionType.KEEP_MINE); 
     572            } 
     573        } 
     574 
     575        public void update(Observable o, Object arg) { 
     576            setEnabled(model.hasVisibleStateConflict() && ! model.isDecidedVisibleState()); 
     577        } 
     578 
     579        protected boolean confirmKeepMine() { 
     580            String [] options = { 
     581                    tr("Yes, reset the id"), 
     582                    tr("No, abort") 
     583            }; 
     584            int ret = JOptionPane.showOptionDialog( 
     585                    null, 
     586                    tr("<html>To keep your local version, JOSM<br>" 
     587                            + "has to reset the id of {0} {1} to 0.<br>" 
     588                            + "On the next upload the server will assign<br>" 
     589                            + "it a new id.<br>" 
     590                            + "Do yo agree?</html>", 
     591                            OsmPrimitiveType.from(model.getMyPrimitive()).getLocalizedDisplayNamePlural(), 
     592                            model.getMyPrimitive().id 
     593                    ), 
     594                    tr("Reset id to 0"), 
     595                    JOptionPane.YES_NO_OPTION, 
     596                    JOptionPane.QUESTION_MESSAGE, 
     597                    null, 
     598                    options, 
     599                    options[1] 
     600            ); 
     601            return ret == JOptionPane.YES_OPTION; 
     602        } 
     603    } 
     604 
     605    class KeepTheirVisibleStateAction extends AbstractAction implements Observer { 
     606        public KeepTheirVisibleStateAction() { 
     607            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagkeeptheir")); 
     608            putValue(Action.SHORT_DESCRIPTION, tr("Keep their visible state")); 
     609        } 
     610 
     611        public void actionPerformed(ActionEvent e) { 
     612            if (confirmKeepTheir()){ 
     613                model.decideVisibleStateConflict(MergeDecisionType.KEEP_THEIR); 
     614            } 
     615        } 
     616 
     617        public void update(Observable o, Object arg) { 
     618            setEnabled(model.hasVisibleStateConflict() && ! model.isDecidedVisibleState()); 
     619        } 
     620 
     621        protected boolean confirmKeepTheir() { 
     622            String [] options = { 
     623                    tr("Yes, purge it"), 
     624                    tr("No, abort") 
     625            }; 
     626            int ret = JOptionPane.showOptionDialog( 
     627                    null, 
     628                    tr("<html>JOSM will have to remove your local primitive with id {0}<br>" 
     629                            + "from the dataset.<br>" 
     630                            + "Do you agree?</html>", 
     631                            model.getMyPrimitive().id 
     632                    ), 
     633                    tr("Remove from dataset"), 
     634                    JOptionPane.YES_NO_OPTION, 
     635                    JOptionPane.QUESTION_MESSAGE, 
     636                    null, 
     637                    options, 
     638                    options[1] 
     639            ); 
     640            return ret == JOptionPane.YES_OPTION; 
     641        } 
     642    } 
     643 
     644    class UndecideVisibleStateConflictAction extends AbstractAction implements Observer { 
     645        public UndecideVisibleStateConflictAction() { 
     646            putValue(Action.SMALL_ICON, ImageProvider.get("dialogs/conflict", "tagundecide")); 
     647            putValue(Action.SHORT_DESCRIPTION, tr("Undecide conflict between visible state")); 
     648        } 
     649 
     650        public void actionPerformed(ActionEvent e) { 
     651            model.decideVisibleStateConflict(MergeDecisionType.UNDECIDED); 
     652        } 
     653 
     654        public void update(Observable o, Object arg) { 
     655            setEnabled(model.hasVisibleStateConflict() && model.isDecidedVisibleState()); 
     656        } 
     657    } 
    420658} 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/ConflictResolutionDialog.java

    r1677 r1690  
    2323import org.openstreetmap.josm.command.Command; 
    2424import org.openstreetmap.josm.gui.conflict.ConflictResolver; 
     25import org.openstreetmap.josm.gui.conflict.properties.OperationCancelledException; 
    2526import org.openstreetmap.josm.tools.ImageProvider; 
    2627 
     
    190191                    return; 
    191192            } 
    192             Command cmd = resolver.buildResolveCommand(); 
    193             Main.main.undoRedo.add(cmd); 
     193            try { 
     194                Command cmd = resolver.buildResolveCommand(); 
     195                Main.main.undoRedo.add(cmd); 
     196            } catch(OperationCancelledException e) { 
     197                // do nothing. Exception already reported 
     198            } 
    194199            setVisible(false); 
    195200        } 
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r1670 r1690  
    527527                    final MergeVisitor visitor = new MergeVisitor(Main.main 
    528528                            .editLayer().data, dataSet); 
    529                     for (final OsmPrimitive osm : dataSet.allPrimitives()) { 
    530                         osm.visit(visitor); 
    531                     } 
    532                     visitor.fixReferences(); 
     529                    visitor.merge(); 
    533530 
    534531                    // copy the merged layer's data source info 
     
    538535                    Main.main.editLayer().fireDataChange(); 
    539536 
    540                     if (visitor.conflicts.isEmpty()) 
     537                    if (visitor.getConflicts().isEmpty()) 
    541538                        return; 
    542539                    final ConflictDialog dlg = Main.map.conflictDialog; 
    543                     dlg.add(visitor.conflicts); 
     540                    dlg.add(visitor.getConflicts()); 
    544541                    JOptionPane.showMessageDialog(Main.parent, 
    545542                            tr("There were conflicts during import.")); 
  • trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java

    r1677 r1690  
    245245    public void mergeFrom(final DataSet from) { 
    246246        final MergeVisitor visitor = new MergeVisitor(data,from); 
    247         for (final OsmPrimitive osm : from.allPrimitives()) { 
    248             osm.visit(visitor); 
    249         } 
    250         visitor.fixReferences(); 
     247        visitor.merge(); 
    251248 
    252249        Area a = data.getDataSourceArea(); 
     
    274271        Main.map.mapView.repaint(); 
    275272 
    276         if (visitor.conflicts.isEmpty()) 
     273        if (visitor.getConflicts().isEmpty()) 
    277274            return; 
    278275        final ConflictDialog dlg = Main.map.conflictDialog; 
    279         dlg.add(visitor.conflicts); 
    280         JOptionPane.showMessageDialog(Main.parent,tr("There were {0} conflicts during import.", visitor.conflicts.size())); 
     276        dlg.add(visitor.getConflicts()); 
     277        JOptionPane.showMessageDialog(Main.parent,tr("There were {0} conflicts during import.", visitor.getConflicts().size())); 
    281278        if (!dlg.isVisible()) { 
    282279            dlg.action.actionPerformed(new ActionEvent(this, 0, "")); 
  • trunk/src/org/openstreetmap/josm/io/OsmApi.java

    r1688 r1690  
    44import static org.openstreetmap.josm.tools.I18n.tr; 
    55 
     6import java.awt.EventQueue; 
    67import java.io.BufferedReader; 
    78import java.io.BufferedWriter; 
     
    323324        } 
    324325        notifyStatusMessage(tr("Uploading...")); 
     326        setAutoProgressIndication(true); 
    325327 
    326328        String diff = duv.getDocument(); 
     
    330332        } catch(Exception e) { 
    331333            throw new OsmTransferException(e); 
     334        } finally { 
     335            setAutoProgressIndication(false); 
    332336        } 
    333337 
     
    481485        Main.pleaseWaitDlg.progress.setValue(current + delta); 
    482486    } 
     487 
     488 
     489    protected void setAutoProgressIndication(final boolean enabled) { 
     490        EventQueue.invokeLater( 
     491                new Runnable() { 
     492                    public void run() { 
     493                        Main.pleaseWaitDlg.setIndeterminate(enabled); 
     494                    } 
     495                } 
     496        ); 
     497    } 
    483498} 
  • trunk/src/org/openstreetmap/josm/io/OsmReader.java

    r1677 r1690  
    99import java.util.ArrayList; 
    1010import java.util.Collection; 
     11import java.util.Date; 
    1112import java.util.HashMap; 
     13import java.util.HashSet; 
    1214import java.util.LinkedList; 
    1315import java.util.Map; 
     16import java.util.Set; 
    1417import java.util.Map.Entry; 
    1518 
     
    2932import org.openstreetmap.josm.data.osm.Way; 
    3033import org.openstreetmap.josm.data.osm.visitor.AddVisitor; 
    31 import org.openstreetmap.josm.data.osm.visitor.Visitor; 
    3234import org.openstreetmap.josm.gui.PleaseWaitDialog; 
    3335import org.openstreetmap.josm.tools.DateUtils; 
     
    4951public class OsmReader { 
    5052 
    51 //     static long tagsN = 0; 
    52 //     static long nodesN = 0; 
    53 //     static long waysN = 0; 
    54 //     static long relationsN = 0; 
    55 //     static long membersN = 0; 
    56  
    57      static InputStream currSource; 
    58  
    59      /** 
    60       * This is used as (readonly) source for finding missing references when not transferred in the 
    61       * file. 
    62       */ 
    63      private DataSet references; 
    64  
    65      /** 
    66       * The dataset to add parsed objects to. 
    67       */ 
    68      private DataSet ds = new DataSet(); 
    69      public DataSet getDs() { return ds; } 
    70  
    71      /** 
    72       * Record warnings.  If there were any data inconsistencies, append 
    73       * a newline-terminated string. 
    74       */ 
    75      private String parseNotes = new String(); 
    76      private int parseNotesCount = 0; 
    77      public String getParseNotes() { 
    78          return parseNotes; 
    79      } 
    80  
    81      /** 
    82       * The visitor to use to add the data to the set. 
    83       */ 
    84      private AddVisitor adder = new AddVisitor(ds); 
    85  
    86      /** 
    87       * All read nodes after phase 1. 
    88       */ 
    89      private Map<Long, Node> nodes = new HashMap<Long, Node>(); 
    90  
    91      // TODO: What the hack? Is this really from me? Please, clean this up! 
    92      private static class OsmPrimitiveData extends OsmPrimitive { 
    93           @Override public void visit(Visitor visitor) {} 
    94           public int compareTo(OsmPrimitive o) {return 0;} 
    95  
    96           public void copyTo(OsmPrimitive osm) { 
    97                osm.id = id; 
    98                osm.keys = keys; 
    99                osm.modified = modified; 
    100                osm.selected = selected; 
    101                osm.deleted = deleted; 
    102                osm.setTimestamp(getTimestamp()); 
    103                osm.user = user; 
    104                osm.visible = visible; 
    105                osm.version = version; 
    106                osm.mappaintStyle = null; 
    107           } 
    108      } 
    109  
    110      /** 
    111       * Used as a temporary storage for relation members, before they 
    112       * are resolved into pointers to real objects. 
    113       */ 
    114      private static class RelationMemberData { 
    115           public String type; 
    116           public long id; 
    117           public RelationMember relationMember; 
    118      } 
    119  
    120      /** 
    121       * Data structure for the remaining way objects 
    122       */ 
    123      private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>(); 
    124  
    125      /** 
    126       * Data structure for relation objects 
    127       */ 
    128      private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>(); 
    129  
    130      private class Parser extends DefaultHandler { 
    131           /** 
    132            * The current osm primitive to be read. 
    133            */ 
    134           private OsmPrimitive current; 
    135           private String generator; 
    136           private Map<String, String> keys = new HashMap<String, String>(); 
    137 //          int n = 0; 
    138  
    139           @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 
    140                try { 
    141 //                    if(n%100000 == 0) { 
    142 //                        try { 
    143 //                            FileInputStream fis = (FileInputStream)currSource; 
    144 //                            FileChannel channel = fis.getChannel(); 
    145 //                            double perc = (((double)channel.position()) / ((double)channel.size()) * 100.0); 
    146 //                            System.out.format(" " + (int)perc + "%%"); 
    147 //                        } 
    148 //                        catch(java.lang.ClassCastException cce) { 
    149 //                        } 
    150 //                        catch(IOException e) { 
    151 //                            System.out.format("Error reading file position " + e); 
    152 //                        } 
    153 //                    } 
    154 //                    n++; 
    155  
    156                     if (qName.equals("osm")) { 
    157                          if (atts == null) 
    158                               throw new SAXException(tr("Unknown version")); 
    159                          String v = atts.getValue("version"); 
    160                          if (v == null) 
    161                              throw new SAXException(tr("Version number missing from OSM data")); 
    162                          if (!(v.equals("0.5") || v.equals("0.6"))) 
    163                              throw new SAXException(tr("Unknown version: {0}", v)); 
    164                          // save generator attribute for later use when creating DataSource objects 
    165                          generator = atts.getValue("generator"); 
    166                          ds.version = v; 
    167  
    168                     } else if (qName.equals("bounds")) { 
    169                          // new style bounds. 
    170                          String minlon = atts.getValue("minlon"); 
    171                          String minlat = atts.getValue("minlat"); 
    172                          String maxlon = atts.getValue("maxlon"); 
    173                          String maxlat = atts.getValue("maxlat"); 
    174                          String origin = atts.getValue("origin"); 
    175                          if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 
    176                               if (origin == null) origin = generator; 
    177                               Bounds bounds = new Bounds( 
    178                                   new LatLon(Double.parseDouble(minlat), Double.parseDouble(minlon)), 
    179                                   new LatLon(Double.parseDouble(maxlat), Double.parseDouble(maxlon))); 
    180                               DataSource src = new DataSource(bounds, origin); 
    181                               ds.dataSources.add(src); 
    182                          } 
     53    /** 
     54     * This is used as (readonly) source for finding missing references when not transferred in the 
     55     * file. 
     56     */ 
     57    private DataSet references; 
     58 
     59    /** 
     60     * The dataset to add parsed objects to. 
     61     */ 
     62    private DataSet ds = new DataSet(); 
     63    public DataSet getDs() { return ds; } 
     64 
     65    /** 
     66     * Record warnings.  If there were any data inconsistencies, append 
     67     * a newline-terminated string. 
     68     */ 
     69    private String parseNotes = new String(); 
     70    private int parseNotesCount = 0; 
     71    public String getParseNotes() { 
     72        return parseNotes; 
     73    } 
     74 
     75    /** the list of ids of skipped {@see Way}s, i.e. ways which referred to nodes 
     76     * not included in the parsed data 
     77     */ 
     78    private Set<Long> skippedWayIds = new HashSet<Long>(); 
     79 
     80    /** 
     81     * The visitor to use to add the data to the set. 
     82     */ 
     83    private AddVisitor adder = new AddVisitor(ds); 
     84 
     85    /** 
     86     * All read nodes after phase 1. 
     87     */ 
     88    private Map<Long, Node> nodes = new HashMap<Long, Node>(); 
     89 
     90 
     91    private static class OsmPrimitiveData { 
     92        public long id = 0; 
     93        public Map<String,String> keys = new HashMap<String, String>(); 
     94        public boolean modified = false; 
     95        public boolean selected = false; 
     96        public boolean deleted = false; 
     97        public Date timestamp = new Date(); 
     98        public User user = null; 
     99        public boolean visible = true; 
     100        public int version = -1; 
     101        public LatLon latlon = new LatLon(0,0); 
     102 
     103        public void copyTo(OsmPrimitive osm) { 
     104            osm.id = id; 
     105            osm.keys = keys; 
     106            osm.modified = modified; 
     107            osm.selected = selected; 
     108            osm.deleted = deleted; 
     109            osm.setTimestamp(timestamp); 
     110            osm.user = user; 
     111            osm.visible = visible; 
     112            osm.version = version; 
     113            osm.mappaintStyle = null; 
     114        } 
     115 
     116        public Node createNode() { 
     117            Node node = new Node(latlon); 
     118            copyTo(node); 
     119            return node; 
     120        } 
     121 
     122        public Way createWay() { 
     123            Way way = new Way(id); 
     124            copyTo(way); 
     125            return way; 
     126        } 
     127 
     128        public Relation createRelation() { 
     129            Relation rel = new Relation(id); 
     130            copyTo(rel); 
     131            return rel; 
     132        } 
     133    } 
     134 
     135    /** 
     136     * Used as a temporary storage for relation members, before they 
     137     * are resolved into pointers to real objects. 
     138     */ 
     139    private static class RelationMemberData { 
     140        public String type; 
     141        public long id; 
     142        public RelationMember relationMember; 
     143    } 
     144 
     145    /** 
     146     * Data structure for the remaining way objects 
     147     */ 
     148    private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>(); 
     149 
     150    /** 
     151     * Data structure for relation objects 
     152     */ 
     153    private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>(); 
     154 
     155    private class Parser extends DefaultHandler { 
     156        /** 
     157         * The current osm primitive to be read. 
     158         */ 
     159        private OsmPrimitiveData current; 
     160        private String generator; 
     161 
     162        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 
     163            try { 
     164                if (qName.equals("osm")) { 
     165                    if (atts == null) 
     166                        throw new SAXException(tr("Unknown version")); 
     167                    String v = atts.getValue("version"); 
     168                    if (v == null) 
     169                        throw new SAXException(tr("Version number missing from OSM data")); 
     170                    if (!(v.equals("0.5") || v.equals("0.6"))) 
     171                        throw new SAXException(tr("Unknown version: {0}", v)); 
     172                    // save generator attribute for later use when creating DataSource objects 
     173                    generator = atts.getValue("generator"); 
     174                    ds.version = v; 
     175 
     176                } else if (qName.equals("bounds")) { 
     177                    // new style bounds. 
     178                    String minlon = atts.getValue("minlon"); 
     179                    String minlat = atts.getValue("minlat"); 
     180                    String maxlon = atts.getValue("maxlon"); 
     181                    String maxlat = atts.getValue("maxlat"); 
     182                    String origin = atts.getValue("origin"); 
     183                    if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 
     184                        if (origin == null) { 
     185                            origin = generator; 
     186                        } 
     187                        Bounds bounds = new Bounds( 
     188                                new LatLon(Double.parseDouble(minlat), Double.parseDouble(minlon)), 
     189                                new LatLon(Double.parseDouble(maxlat), Double.parseDouble(maxlon))); 
     190                        DataSource src = new DataSource(bounds, origin); 
     191                        ds.dataSources.add(src); 
     192                    } 
    183193 
    184194                    // ---- PARSING NODES AND WAYS ---- 
    185195 
    186                     } else if (qName.equals("node")) { 
    187 //                         nodesN++; 
    188                          current = new Node(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon"))); 
    189                          readCommon(atts, current); 
    190                          nodes.put(current.id, (Node)current); 
    191                     } else if (qName.equals("way")) { 
    192 //                         waysN++; 
    193                          current = new OsmPrimitiveData(); 
    194                          readCommon(atts, current); 
    195                          ways.put((OsmPrimitiveData)current, new ArrayList<Long>()); 
    196                     } else if (qName.equals("nd")) { 
    197                          Collection<Long> list = ways.get(current); 
    198                          if (list == null) 
    199                               throw new SAXException(tr("Found <nd> element in non-way.")); 
    200                          long id = getLong(atts, "ref"); 
    201                          if (id == 0) 
    202                               throw new SAXException(tr("<nd> has zero ref")); 
    203                          list.add(id); 
     196                } else if (qName.equals("node")) { 
     197                    current = new OsmPrimitiveData(); 
     198                    current.latlon = new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon")); 
     199                    readCommon(atts, current); 
     200                } else if (qName.equals("way")) { 
     201                    current = new OsmPrimitiveData(); 
     202                    readCommon(atts, current); 
     203                    ways.put(current, new ArrayList<Long>()); 
     204                } else if (qName.equals("nd")) { 
     205                    Collection<Long> list = ways.get(current); 
     206                    if (list == null) 
     207                        throw new SAXException(tr("Found <nd> element in non-way.")); 
     208                    long id = getLong(atts, "ref"); 
     209                    if (id == 0) 
     210                        throw new SAXException(tr("<nd> has zero ref")); 
     211                    list.add(id); 
    204212 
    205213                    // ---- PARSING RELATIONS ---- 
    206214 
    207                     } else if (qName.equals("relation")) { 
    208                          current = new OsmPrimitiveData(); 
    209                          readCommon(atts, current); 
    210                          relations.put((OsmPrimitiveData)current, new LinkedList<RelationMemberData>()); 
    211                     } else if (qName.equals("member")) { 
    212                          Collection<RelationMemberData> list = relations.get(current); 
    213                          if (list == null) 
    214                               throw new SAXException(tr("Found <member> element in non-relation.")); 
    215                          RelationMemberData emd = new RelationMemberData(); 
    216                          emd.relationMember = new RelationMember(); 
    217                          String value = atts.getValue("ref"); 
    218                          if (value == null) { 
    219                              throw new SAXException(tr("Missing attribute \"ref\" on member in relation {0}",current.id)); 
    220                          } 
    221                          try { 
    222                              emd.id = Long.parseLong(value); 
    223                          } catch(NumberFormatException e) { 
    224                              throw new SAXException(tr("Illegal value for attribute \"ref\" on member in relation {0}, got {1}", Long.toString(current.id),value)); 
    225                          } 
    226                          value = atts.getValue("type"); 
    227                          if (value == null) { 
    228                              throw new SAXException(tr("Missing attribute \"type\" on member {0} in relation {1}", Long.toString(emd.id), Long.toString(current.id))); 
    229                          } 
    230                          if (! (value.equals("way") || value.equals("node") || value.equals("relation"))) { 
    231                              throw new SAXException(tr("Unexpected \"type\" on member {0} in relation {1}, got {2}.", Long.toString(emd.id), Long.toString(current.id), value)); 
    232                          } 
    233                          emd.type= value; 
    234                          value = atts.getValue("role"); 
    235                          emd.relationMember.role = value; 
    236  
    237                          if (emd.id == 0) 
    238                               throw new SAXException(tr("Incomplete <member> specification with ref=0")); 
    239  
    240                          list.add(emd); 
     215                } else if (qName.equals("relation")) { 
     216                    current = new OsmPrimitiveData(); 
     217                    readCommon(atts, current); 
     218                    relations.put(current, new LinkedList<RelationMemberData>()); 
     219                } else if (qName.equals("member")) { 
     220                    Collection<RelationMemberData> list = relations.get(current); 
     221                    if (list == null) 
     222                        throw new SAXException(tr("Found <member> element in non-relation.")); 
     223                    RelationMemberData emd = new RelationMemberData(); 
     224                    emd.relationMember = new RelationMember(); 
     225                    String value = atts.getValue("ref"); 
     226                    if (value == null) 
     227                        throw new SAXException(tr("Missing attribute \"ref\" on member in relation {0}",current.id)); 
     228                    try { 
     229                        emd.id = Long.parseLong(value); 
     230                    } catch(NumberFormatException e) { 
     231                        throw new SAXException(tr("Illegal value for attribute \"ref\" on member in relation {0}, got {1}", Long.toString(current.id),value)); 
     232                    } 
     233                    value = atts.getValue("type"); 
     234                    if (value == null) 
     235                        throw new SAXException(tr("Missing attribute \"type\" on member {0} in relation {1}", Long.toString(emd.id), Long.toString(current.id))); 
     236                    if (! (value.equals("way") || value.equals("node") || value.equals("relation"))) 
     237                        throw new SAXException(tr("Unexpected \"type\" on member {0} in relation {1}, got {2}.", Long.toString(emd.id), Long.toString(current.id), value)); 
     238                    emd.type= value; 
     239                    value = atts.getValue("role"); 
     240                    emd.relationMember.role = value; 
     241 
     242                    if (emd.id == 0) 
     243                        throw new SAXException(tr("Incomplete <member> specification with ref=0")); 
     244 
     245                    list.add(emd); 
    241246 
    242247                    // ---- PARSING TAGS (applicable to all objects) ---- 
    243248 
    244                     } else if (qName.equals("tag")) { 
    245 //                         tagsN++; 
    246                         String key = atts.getValue("k"); 
    247                         String internedKey = keys.get(key); 
    248                         if (internedKey == null) { 
    249                             internedKey = key; 
    250                             keys.put(key, key); 
    251                         } 
    252                          current.put(internedKey, atts.getValue("v")); 
    253                     } 
    254                } catch (NumberFormatException x) { 
    255                     x.printStackTrace(); // SAXException does not chain correctly 
    256                     throw new SAXException(x.getMessage(), x); 
    257                } catch (NullPointerException x) { 
    258                     x.printStackTrace(); // SAXException does not chain correctly 
    259                     throw new SAXException(tr("NullPointerException, possibly some missing tags."), x); 
    260                } 
    261           } 
    262  
    263           private double getDouble(Attributes atts, String value) { 
    264                return Double.parseDouble(atts.getValue(value)); 
    265           } 
    266      } 
    267  
    268      /** 
    269       * Read out the common attributes from atts and put them into this.current. 
    270       */ 
    271      void readCommon(Attributes atts, OsmPrimitive current) throws SAXException { 
    272           current.id = getLong(atts, "id"); 
    273           if (current.id == 0) 
    274                throw new SAXException(tr("Illegal object with id=0")); 
    275  
    276           String time = atts.getValue("timestamp"); 
    277           if (time != null && time.length() != 0) { 
    278                current.setTimestamp(DateUtils.fromString(time)); 
    279           } 
    280  
    281           // user attribute added in 0.4 API 
    282           String user = atts.getValue("user"); 
    283           if (user != null) { 
    284                // do not store literally; get object reference for string 
    285                current.user = User.get(user); 
    286           } 
    287  
    288           // uid attribute added in 0.6 API 
    289           String uid = atts.getValue("uid"); 
    290           if (uid != null) { 
    291               if (current.user != null) { 
    292                   current.user.uid = uid; 
    293               } 
    294          } 
    295  
    296           // visible attribute added in 0.4 API 
    297           String visible = atts.getValue("visible"); 
    298           if (visible != null) { 
    299                current.visible = Boolean.parseBoolean(visible); 
    300           } 
    301  
    302           String version = atts.getValue("version"); 
    303           current.version = 0; 
    304           if (version != null) { 
    305               try { 
    306                   current.version = Integer.parseInt(version); 
    307               } catch(NumberFormatException e) { 
    308                   throw new SAXException(tr("Illegal value for attribute \"version\" on OSM primitive with id {0}, got {1}", Long.toString(current.id), version)); 
    309               } 
    310           } else { 
    311               // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 
    312               // 
    313               if (current.id > 0 && ds.version != null && ds.version.equals("0.6")) { 
    314                   throw new SAXException(tr("Missing attribute \"version\" on OSM primitive with id {0}", Long.toString(current.id))); 
    315               } 
    316           } 
    317  
    318           String action = atts.getValue("action"); 
    319           if (action == null) 
    320                return; 
    321           if (action.equals("delete")) 
    322                current.delete(true); 
    323           else if (action.startsWith("modify")) 
    324                current.modified = true; 
    325      } 
    326      private long getLong(Attributes atts, String value) throws SAXException { 
    327           String s = atts.getValue(value); 
    328           if (s == null) 
    329                throw new SAXException(tr("Missing required attribute \"{0}\".",value)); 
    330           return Long.parseLong(s); 
    331      } 
    332  
    333      private Node findNode(long id) { 
    334          Node n = nodes.get(id); 
    335          if (n != null) 
    336               return n; 
    337          for (Node node : references.nodes) 
    338               if (node.id == id) 
    339                    return node; 
    340          // TODO: This has to be changed to support multiple layers. 
    341          for (Node node : Main.ds.nodes) 
    342               if (node.id == id) 
    343                    return new Node(node); 
    344          return null; 
    345     } 
    346  
    347      private void createWays() { 
    348           for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) { 
    349                Way w = new Way(); 
    350                boolean failed = false; 
    351                for (long id : e.getValue()) { 
    352                     Node n = findNode(id); 
    353                     if (n == null) { 
    354                          /* don't report ALL of them, just a few */ 
    355                          if (parseNotesCount++ < 6) { 
    356                              parseNotes += tr("Skipping a way because it includes a node that doesn''t exist: {0}\n", id); 
    357                          } else if (parseNotesCount == 6) { 
    358                              parseNotes += "...\n"; 
    359                          } 
    360                          failed = true; 
    361                          break; 
    362                     } 
    363                     w.nodes.add(n); 
    364                } 
    365                if (failed) continue; 
    366                e.getKey().copyTo(w); 
    367                adder.visit(w); 
    368           } 
    369  
    370      } 
    371  
    372      /** 
    373       * Return the Way object with the given id, or null if it doesn't 
    374       * exist yet. This method only looks at ways stored in the data set. 
    375       * 
    376       * @param id 
    377       * @return way object or null 
    378       */ 
    379      private Way findWay(long id) { 
    380           for (Way wy : Main.ds.ways) 
    381                if (wy.id == id) 
    382                     return wy; 
    383           return null; 
    384      } 
    385  
    386      /** 
    387       * Return the Relation object with the given id, or null if it doesn't 
    388       * exist yet. This method only looks at relations stored in the data set. 
    389       * 
    390       * @param id 
    391       * @return relation object or null 
    392       */ 
    393      private Relation findRelation(long id) { 
    394           for (Relation e : ds.relations) 
    395                if (e.id == id) 
    396                     return e; 
    397           for (Relation e : Main.ds.relations) 
    398                if (e.id == id) 
    399                     return e; 
    400           return null; 
    401      } 
    402  
    403      /** 
    404       * Create relations. This is slightly different than n/s/w because 
    405       * unlike other objects, relations may reference other relations; it 
    406       * is not guaranteed that a referenced relation will have been created 
    407       * before it is referenced. So we have to create all relations first, 
    408       * and populate them later. 
    409       */ 
    410      private void createRelations() { 
    411  
    412           // pass 1 - create all relations 
    413           for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) { 
    414                Relation en = new Relation(); 
    415                e.getKey().copyTo(en); 
    416                adder.visit(en); 
    417           } 
    418  
    419           // Cache the ways here for much better search performance 
    420           HashMap<Long, Way> hm = new HashMap<Long, Way>(10000); 
    421           for (Way wy : ds.ways) 
     249                } else if (qName.equals("tag")) { 
     250                    String key = atts.getValue("k"); 
     251                    String value = atts.getValue("v"); 
     252                    current.keys.put(key,value); 
     253                } 
     254            } catch (NumberFormatException x) { 
     255                x.printStackTrace(); // SAXException does not chain correctly 
     256                throw new SAXException(x.getMessage(), x); 
     257            } catch (NullPointerException x) { 
     258                x.printStackTrace(); // SAXException does not chain correctly 
     259                throw new SAXException(tr("NullPointerException, possibly some missing tags."), x); 
     260            } 
     261        } 
     262 
     263        @Override 
     264        public void endElement(String uri, String localName, String qName) throws SAXException { 
     265            if (qName.equals("node")) { 
     266                nodes.put(current.id, current.createNode()); 
     267            } 
     268        } 
     269 
     270        private double getDouble(Attributes atts, String value) { 
     271            return Double.parseDouble(atts.getValue(value)); 
     272        } 
     273    } 
     274 
     275    /** 
     276     * Read out the common attributes from atts and put them into this.current. 
     277     */ 
     278    void readCommon(Attributes atts, OsmPrimitiveData current) throws SAXException { 
     279        current.id = getLong(atts, "id"); 
     280        if (current.id == 0) 
     281            throw new SAXException(tr("Illegal object with id=0")); 
     282 
     283        String time = atts.getValue("timestamp"); 
     284        if (time != null && time.length() != 0) { 
     285            current.timestamp =  DateUtils.fromString(time); 
     286        } 
     287 
     288        // user attribute added in 0.4 API 
     289        String user = atts.getValue("user"); 
     290        if (user != null) { 
     291            // do not store literally; get object reference for string 
     292            current.user = User.get(user); 
     293        } 
     294 
     295        // uid attribute added in 0.6 API 
     296        String uid = atts.getValue("uid"); 
     297        if (uid != null) { 
     298            if (current.user != null) { 
     299                current.user.uid = uid; 
     300            } 
     301        } 
     302 
     303        // visible attribute added in 0.4 API 
     304        String visible = atts.getValue("visible"); 
     305        if (visible != null) { 
     306            current.visible = Boolean.parseBoolean(visible); 
     307        } 
     308 
     309        String version = atts.getValue("version"); 
     310        current.version = 0; 
     311        if (version != null) { 
     312            try { 
     313                current.version = Integer.parseInt(version); 
     314            } catch(NumberFormatException e) { 
     315                throw new SAXException(tr("Illegal value for attribute \"version\" on OSM primitive with id {0}, got {1}", Long.toString(current.id), version)); 
     316            } 
     317        } else { 
     318            // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 
     319            // 
     320            if (current.id > 0 && ds.version != null && ds.version.equals("0.6")) 
     321                throw new SAXException(tr("Missing attribute \"version\" on OSM primitive with id {0}", Long.toString(current.id))); 
     322        } 
     323 
     324        String action = atts.getValue("action"); 
     325        if (action == null) 
     326            return; 
     327        if (action.equals("delete")) { 
     328            current.deleted = true; 
     329        } else if (action.startsWith("modify")) { 
     330            current.modified = true; 
     331        } 
     332    } 
     333    private long getLong(Attributes atts, String value) throws SAXException { 
     334        String s = atts.getValue(value); 
     335        if (s == null) 
     336            throw new SAXException(tr("Missing required attribute \"{0}\".",value)); 
     337        return Long.parseLong(s); 
     338    } 
     339 
     340    private Node findNode(long id) { 
     341        Node n = nodes.get(id); 
     342        if (n != null) 
     343            return n; 
     344        for (Node node : references.nodes) 
     345            if (node.id == id) 
     346                return node; 
     347        // TODO: This has to be changed to support multiple layers. 
     348        for (Node node : Main.ds.nodes) 
     349            if (node.id == id) 
     350                return new Node(node); 
     351        return null; 
     352    } 
     353 
     354    protected void createWays() { 
     355        for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) { 
     356            Way w = new Way(); 
     357            boolean failed = false; 
     358            for (long id : e.getValue()) { 
     359                Node n = findNode(id); 
     360                if (n == null) { 
     361                    /* don't report ALL of them, just a few */ 
     362                    if (parseNotesCount++ < 6) { 
     363                        parseNotes += tr("Skipping a way because it includes a node that doesn''t exist: {0}\n", id); 
     364                    } else if (parseNotesCount == 6) { 
     365                        parseNotes += "...\n"; 
     366                    } 
     367                    failed = true; 
     368                    break; 
     369                } 
     370                w.nodes.add(n); 
     371            } 
     372            if (failed) { 
     373                skippedWayIds.add(e.getKey().id); 
     374                continue; 
     375            } 
     376            e.getKey().copyTo(w); 
     377            adder.visit(w); 
     378        } 
     379 
     380    } 
     381 
     382    /** 
     383     * Return the Way object with the given id, or null if it doesn't 
     384     * exist yet. This method only looks at ways stored in the data set. 
     385     * 
     386     * @param id 
     387     * @return way object or null 
     388     */ 
     389    private Way findWay(long id) { 
     390        for (Way wy : Main.ds.ways) 
     391            if (wy.id == id) 
     392                return wy; 
     393        return null; 
     394    } 
     395 
     396    /** 
     397     * Return the Relation object with the given id, or null if it doesn't 
     398     * exist yet. This method only looks at relations stored in the data set. 
     399     * 
     400     * @param id 
     401     * @return relation object or null 
     402     */ 
     403    private Relation findRelation(long id) { 
     404        for (Relation e : ds.relations) 
     405            if (e.id == id) 
     406                return e; 
     407        for (Relation e : Main.ds.relations) 
     408            if (e.id == id) 
     409                return e; 
     410        return null; 
     411    } 
     412 
     413    /** 
     414     * Create relations. This is slightly different than n/s/w because 
     415     * unlike other objects, relations may reference other relations; it 
     416     * is not guaranteed that a referenced relation will have been created 
     417     * before it is referenced. So we have to create all relations first, 
     418     * and populate them later. 
     419     */ 
     420    private void createRelations() { 
     421 
     422        // pass 1 - create all relations 
     423        for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) { 
     424            Relation en = new Relation(); 
     425            e.getKey().copyTo(en); 
     426            adder.visit(en); 
     427        } 
     428 
     429        // Cache the ways here for much better search performance 
     430        HashMap<Long, Way> hm = new HashMap<Long, Way>(10000); 
     431        for (Way wy : ds.ways) { 
    422432            hm.put(wy.id, wy); 
    423  
    424           // pass 2 - sort out members 
    425           for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) { 
    426                Relation en = findRelation(e.getKey().id); 
    427                if (en == null) throw new Error("Failed to create relation " + e.getKey().id); 
    428  
    429                for (RelationMemberData emd : e.getValue()) { 
    430                     RelationMember em = emd.relationMember; 
    431                     if (emd.type.equals("node")) { 
    432                          em.member = findNode(emd.id); 
    433                          if (em.member == null) { 
    434                               em.member = new Node(emd.id); 
    435                               adder.visit((Node)em.member); 
    436                          } 
    437                     } else if (emd.type.equals("way")) { 
    438                          em.member = hm.get(emd.id); 
    439                          if (em.member == null) 
    440                             em.member = findWay(emd.id); 
    441                          if (em.member == null) { 
    442                               em.member = new Way(emd.id); 
    443                               adder.visit((Way)em.member); 
    444                          } 
    445                     } else if (emd.type.equals("relation")) { 
    446                          em.member = findRelation(emd.id); 
    447                          if (em.member == null) { 
    448                               em.member = new Relation(emd.id); 
    449                               adder.visit((Relation)em.member); 
    450                          } 
    451                     } else { 
    452                          // this is an error. 
    453                     } 
    454                     en.members.add(em); 
    455                } 
    456           } 
    457           hm = null; 
    458      } 
    459  
    460      /** 
    461       * Parse the given input source and return the dataset. 
    462       * @param ref The dataset that is search in for references first. If 
    463       *      the Reference is not found here, Main.ds is searched and a copy of the 
    464       *  element found there is returned. 
    465       */ 
    466      public static DataSet parseDataSet(InputStream source, DataSet ref, PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException { 
    467           return parseDataSetOsm(source, ref, pleaseWaitDlg).ds; 
    468      } 
    469  
    470      public static OsmReader parseDataSetOsm(InputStream source, DataSet ref, PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException { 
    471           OsmReader osm = new OsmReader(); 
    472           osm.references = ref == null ? new DataSet() : ref; 
    473  
    474           currSource = source; 
    475  
    476           // phase 1: Parse nodes and read in raw ways 
    477           InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8")); 
    478           try { 
    479              SAXParserFactory.newInstance().newSAXParser().parse(inputSource, osm.new Parser()); 
    480           } catch (ParserConfigurationException e1) { 
    481              e1.printStackTrace(); // broken SAXException chaining 
    482              throw new SAXException(e1); 
    483           } 
    484  
    485           Main.pleaseWaitDlg.currentAction.setText(tr("Prepare OSM data...")); 
    486           Main.pleaseWaitDlg.setIndeterminate(true); 
    487  
    488 //          System.out.println("Parser finished: Tags " + tagsN + " Nodes " + nodesN + " Ways " + waysN + 
    489 //            " Relations " + relationsN + " Members " + membersN); 
    490  
    491           for (Node n : osm.nodes.values()) 
    492                osm.adder.visit(n); 
    493  
    494           try { 
    495                osm.createWays(); 
    496                osm.createRelations(); 
    497           } catch (NumberFormatException e) { 
    498                e.printStackTrace(); 
    499                throw new SAXException(tr("Ill-formed node id")); 
    500           } 
    501  
    502           // clear all negative ids (new to this file) 
    503           for (OsmPrimitive o : osm.ds.allPrimitives()) 
    504                if (o.id < 0) 
    505                     o.id = 0; 
    506  
    507 //          System.out.println("Data loaded!"); 
    508           Main.pleaseWaitDlg.setIndeterminate(false); 
    509           Main.pleaseWaitDlg.progress.setValue(0); 
    510  
    511           return osm; 
    512      } 
     433        } 
     434 
     435        // pass 2 - sort out members 
     436        for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) { 
     437            Relation en = findRelation(e.getKey().id); 
     438            if (en == null) throw new Error("Failed to create relation " + e.getKey().id); 
     439 
     440            for (RelationMemberData emd : e.getValue()) { 
     441                RelationMember em = emd.relationMember; 
     442                if (emd.type.equals("node")) { 
     443                    em.member = findNode(emd.id); 
     444                    if (em.member == null) { 
     445                        em.member = new Node(emd.id); 
     446                        adder.visit((Node)em.member); 
     447                    } 
     448                } else if (emd.type.equals("way")) { 
     449                    em.member = hm.get(emd.id); 
     450                    if (em.member == null) { 
     451                        em.member = findWay(emd.id); 
     452                    } 
     453                    if (em.member == null) { 
     454                        em.member = new Way(emd.id); 
     455                        adder.visit((Way)em.member); 
     456                    } 
     457                } else if (emd.type.equals("relation")) { 
     458                    em.member = findRelation(emd.id); 
     459                    if (em.member == null) { 
     460                        em.member = new Relation(emd.id); 
     461                        adder.visit((Relation)em.member); 
     462                    } 
     463                } else { 
     464                    // this is an error. 
     465                } 
     466                en.members.add(em); 
     467            } 
     468        } 
     469        hm = null; 
     470    } 
     471 
     472    /** 
     473     * Parse the given input source and return the dataset. 
     474     * @param ref The dataset that is search in for references first. If 
     475     *      the Reference is not found here, Main.ds is searched and a copy of the 
     476     *  element found there is returned. 
     477     */ 
     478    public static DataSet parseDataSet(InputStream source, DataSet ref, PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException { 
     479        return parseDataSetOsm(source, ref, pleaseWaitDlg).ds; 
     480    } 
     481 
     482    public static OsmReader parseDataSetOsm(InputStream source, DataSet ref, PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException { 
     483        OsmReader osm = new OsmReader(); 
     484        osm.references = ref == null ? new DataSet() : ref; 
     485 
     486        // phase 1: Parse nodes and read in raw ways 
     487        InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8")); 
     488        try { 
     489            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, osm.new Parser()); 
     490        } catch (ParserConfigurationException e1) { 
     491            e1.printStackTrace(); // broken SAXException chaining 
     492            throw new SAXException(e1); 
     493        } 
     494 
     495        Main.pleaseWaitDlg.currentAction.setText(tr("Prepare OSM data...")); 
     496        Main.pleaseWaitDlg.setIndeterminate(true); 
     497 
     498        for (Node n : osm.nodes.values()) { 
     499            osm.adder.visit(n); 
     500        } 
     501 
     502        try { 
     503            osm.createWays(); 
     504            osm.createRelations(); 
     505        } catch (NumberFormatException e) { 
     506            e.printStackTrace(); 
     507            throw new SAXException(tr("Ill-formed node id")); 
     508        } 
     509 
     510        // clear all negative ids (new to this file) 
     511        for (OsmPrimitive o : osm.ds.allPrimitives()) 
     512            if (o.id < 0) { 
     513                o.id = 0; 
     514            } 
     515 
     516        Main.pleaseWaitDlg.setIndeterminate(false); 
     517        Main.pleaseWaitDlg.progress.setValue(0); 
     518 
     519        return osm; 
     520    } 
     521 
     522    /** 
     523     * replies a set of ids of skipped {@see Way}s, i.e. ways which were included in the downloaded 
     524     * data but which referred to nodes <strong>not</strong>  available in the downloaded data 
     525     *  
     526     * @return the set of ids 
     527     */ 
     528    public Set<Long> getSkippedWayIds() { 
     529        return skippedWayIds; 
     530    } 
    513531} 
Note: See TracChangeset for help on using the changeset viewer.