Changeset 2070 in josm


Ignore:
Timestamp:
2009-09-06T23:07:33+02:00 (10 years ago)
Author:
Gubaer
Message:

new: rewrite of CombineWay action
new: conflict resolution dialog for CombineWay, including conflicts for different relation memberships
cleanup: cleanup in OsmReader, reduces memory footprint and reduces parsing time
cleanup: made most of the public fields in OsmPrimitive @deprecated, added accessors and changed the code
cleanup: replaced usages of @deprecated constructors for ExtendedDialog
fixed #3208: Combine ways brokes relation order

WARNING: this changeset touches a lot of code all over the code base. "latest" might become slightly unstable in the next days. Also experience incompatibility issues with plugins in the next few days.

Location:
trunk/src/org/openstreetmap/josm
Files:
12 added
76 edited

Legend:

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

    r2047 r2070  
    5858import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
    5959import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
     60import org.openstreetmap.josm.io.IllegalDataException;
    6061import org.openstreetmap.josm.plugins.PluginHandler;
    6162import org.openstreetmap.josm.tools.ImageProvider;
     
    503504                    OpenFileAction.openFile(f);
    504505                }
     506            } catch(IllegalDataException e) {
     507                e.printStackTrace();
     508                JOptionPane.showMessageDialog(
     509                        Main.parent,
     510                        tr("<html>Could not read file ''{0}\''.<br> Error is: <br>{1}</html>", f.getName(), e.getMessage()),
     511                        tr("Error"),
     512                        JOptionPane.ERROR_MESSAGE
     513                );
    505514            }catch(IOException e) {
    506515                e.printStackTrace();
     
    527536        try {
    528537            OpenFileAction.openFile(f);
     538        }catch(IllegalDataException e) {
     539            e.printStackTrace();
     540            JOptionPane.showMessageDialog(
     541                    Main.parent,
     542                    tr("<html>Could not read file ''{0}\''.<br> Error is: <br>{1}</html>", f.getName(), e.getMessage()),
     543                    tr("Error"),
     544                    JOptionPane.ERROR_MESSAGE
     545            );
    529546        }catch(IOException e) {
    530547            e.printStackTrace();
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r2032 r2070  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.GridBagLayout;
    76import java.awt.event.ActionEvent;
    87import java.awt.event.KeyEvent;
     
    1312import java.util.LinkedList;
    1413import java.util.List;
    15 import java.util.ListIterator;
    1614import java.util.Map;
    1715import java.util.Set;
    18 import java.util.TreeMap;
    19 import java.util.TreeSet;
    20 import java.util.Map.Entry;
    21 
    22 import javax.swing.Box;
    23 import javax.swing.JComboBox;
    24 import javax.swing.JLabel;
     16import java.util.Stack;
     17
    2518import javax.swing.JOptionPane;
    26 import javax.swing.JPanel;
     19import javax.swing.SwingUtilities;
    2720
    2821import org.openstreetmap.josm.Main;
     
    3124import org.openstreetmap.josm.command.DeleteCommand;
    3225import org.openstreetmap.josm.command.SequenceCommand;
     26import org.openstreetmap.josm.data.osm.DataSet;
    3327import org.openstreetmap.josm.data.osm.Node;
    3428import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3529import org.openstreetmap.josm.data.osm.Relation;
    3630import org.openstreetmap.josm.data.osm.RelationMember;
    37 import org.openstreetmap.josm.data.osm.TigerUtils;
     31import org.openstreetmap.josm.data.osm.Tag;
     32import org.openstreetmap.josm.data.osm.TagCollection;
    3833import org.openstreetmap.josm.data.osm.Way;
    3934import org.openstreetmap.josm.gui.ExtendedDialog;
    40 import org.openstreetmap.josm.tools.GBC;
     35import org.openstreetmap.josm.gui.conflict.tags.CombineWaysConflictResolverDialog;
    4136import org.openstreetmap.josm.tools.Pair;
    4237import org.openstreetmap.josm.tools.Shortcut;
     
    4540 * Combines multiple ways into one.
    4641 *
    47  * @author Imi
    4842 */
    4943public class CombineWayAction extends JosmAction {
     
    5448    }
    5549
    56     @SuppressWarnings("unchecked")
     50    protected Set<OsmPrimitive> intersect(Set<? extends OsmPrimitive> s1, Set<? extends OsmPrimitive> s2) {
     51        HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>(s1);
     52        ret.retainAll(s2);
     53        return ret;
     54    }
     55
     56    protected boolean confirmCombiningWithConflictsInRelationMemberships() {
     57        ExtendedDialog ed = new ExtendedDialog(Main.parent,
     58                tr("Combine ways with different memberships?"),
     59                new String[] {tr("Combine Anyway"), tr("Cancel")});
     60        ed.setButtonIcons(new String[] {"combineway.png", "cancel.png"});
     61        ed.setContent(tr("The selected ways have differing relation memberships.  "
     62                + "Do you still want to combine them?"));
     63        ed.showDialog();
     64
     65        return ed.getValue() == 1;
     66    }
     67
     68    protected boolean confirmChangeDirectionOfWays() {
     69        ExtendedDialog ed = new ExtendedDialog(Main.parent,
     70                tr("Change directions?"),
     71                new String[] {tr("Reverse and Combine"), tr("Cancel")});
     72        ed.setButtonIcons(new String[] {"wayflip.png", "cancel.png"});
     73        ed.setContent(tr("The ways can not be combined in their current directions.  "
     74                + "Do you want to reverse some of them?"));
     75        ed.showDialog();
     76        return ed.getValue() == 1;
     77    }
     78
     79    protected void warnCombiningImpossible() {
     80        String msg = tr("Could not combine ways "
     81                + "(They could not be merged into a single string of nodes)");
     82        JOptionPane.showMessageDialog(
     83                Main.parent,
     84                msg,  //FIXME: not sure whether this fits in a dialog
     85                tr("Information"),
     86                JOptionPane.INFORMATION_MESSAGE
     87        );
     88        return;
     89    }
     90
     91    protected Way getTargetWay(Collection<Way> combinedWays) {
     92        // init with an arbitrary way
     93        Way targetWay = combinedWays.iterator().next();
     94
     95        // look for the first way already existing on
     96        // the server
     97        for (Way w : combinedWays) {
     98            targetWay = w;
     99            if (w.getId() != 0) {
     100                break;
     101            }
     102        }
     103        return targetWay;
     104    }
     105
     106    protected void completeTagCollectionWithMissingTags(TagCollection tc, Collection<Way> combinedWays) {
     107        for (String key: tc.getKeys()) {
     108            // make sure the empty value is in the tag set such that we can delete the tag
     109            // in the conflict dialog if necessary
     110            //
     111            tc.add(new Tag(key,""));
     112            for (Way w: combinedWays) {
     113                if (w.get(key) == null) {
     114                    tc.add(new Tag(key)); // add a tag with key and empty value
     115                }
     116            }
     117        }
     118        // remove irrelevant tags
     119        //
     120        tc.removeByKey("created_by");
     121    }
     122
     123    public void combineWays(Collection<Way> ways) {
     124
     125        // prepare and clean the list of ways to combine
     126        //
     127        if (ways == null || ways.isEmpty())
     128            return;
     129        ways.remove(null); // just in case -  remove all null ways from the collection
     130        ways = new HashSet<Way>(ways); // remove duplicates
     131
     132        // build the list of relations referring to the ways to combine
     133        //
     134        WayReferringRelations referringRelations = new WayReferringRelations(ways);
     135        referringRelations.build(getCurrentDataSet());
     136
     137        // build the collection of tags used by the ways to combine
     138        //
     139        TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways);
     140        completeTagCollectionWithMissingTags(wayTags, ways);
     141
     142        // try to build a new way out of the combination of ways
     143        // which are combined
     144        //
     145        NodeGraph graph = NodeGraph.createDirectedGraphFromWays(ways);
     146        List<Node> path = graph.buildSpanningPath();
     147        if (path == null) {
     148            graph = NodeGraph.createUndirectedGraphFromNodeWays(ways);
     149            path = graph.buildSpanningPath();
     150            if (path != null) {
     151                if (!confirmChangeDirectionOfWays())
     152                    return;
     153            } else {
     154                warnCombiningImpossible();
     155                return;
     156            }
     157        }
     158
     159        // create the new way and apply the new node list
     160        //
     161        Way targetWay = getTargetWay(ways);
     162        Way modifiedTargetWay = new Way(targetWay);
     163        modifiedTargetWay.setNodes(path);
     164
     165        CombineWaysConflictResolverDialog dialog = CombineWaysConflictResolverDialog.getInstance();
     166        dialog.getTagConflictResolverModel().populate(wayTags);
     167        dialog.setTargetWay(targetWay);
     168        dialog.getRelationMemberConflictResolverModel().populate(
     169                referringRelations.getRelations(),
     170                referringRelations.getWays()
     171        );
     172        dialog.prepareDefaultDecisions();
     173
     174        // resolve tag conflicts if necessary
     175        //
     176        if (!wayTags.isApplicableToPrimitive() || !referringRelations.getRelations().isEmpty()) {
     177            dialog.setVisible(true);
     178            if (dialog.isCancelled())
     179                return;
     180        }
     181
     182
     183
     184        LinkedList<Command> cmds = new LinkedList<Command>();
     185        LinkedList<Way> deletedWays = new LinkedList<Way>(ways);
     186        deletedWays.remove(targetWay);
     187
     188        cmds.add(new DeleteCommand(deletedWays));
     189        cmds.add(new ChangeCommand(targetWay, modifiedTargetWay));
     190        cmds.addAll(dialog.buildResolutionCommands(targetWay));
     191        final SequenceCommand sequenceCommand = new SequenceCommand(tr("Combine {0} ways", ways.size()), cmds);
     192
     193        // update gui
     194        final Way selectedWay = targetWay;
     195        Runnable guiTask = new Runnable() {
     196            public void run() {
     197                Main.main.undoRedo.add(sequenceCommand);
     198                getCurrentDataSet().setSelected(selectedWay);
     199            }
     200        };
     201        if (SwingUtilities.isEventDispatchThread()) {
     202            guiTask.run();
     203        } else {
     204            SwingUtilities.invokeLater(guiTask);
     205        }
     206    }
     207
     208
    57209    public void actionPerformed(ActionEvent event) {
    58210        if (getCurrentDataSet() == null)
    59211            return;
    60212        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
    61         LinkedList<Way> selectedWays = new LinkedList<Way>();
    62 
    63         for (OsmPrimitive osm : selection)
    64             if (osm instanceof Way) {
    65                 selectedWays.add((Way)osm);
    66             }
    67 
     213        Set<Way> selectedWays = OsmPrimitive.getFilteredSet(selection, Way.class);
    68214        if (selectedWays.size() < 2) {
    69215            JOptionPane.showMessageDialog(
     
    75221            return;
    76222        }
    77 
    78         // Check whether all ways have identical relationship membership. More
    79         // specifically: If one of the selected ways is a member of relation X
    80         // in role Y, then all selected ways must be members of X in role Y.
    81 
    82         // FIXME: In a later revision, we should display some sort of conflict
    83         // dialog like we do for tags, to let the user choose which relations
    84         // should be kept.
    85 
    86         // Step 1, iterate over all relations and figure out which of our
    87         // selected ways are members of a relation.
    88         HashMap<Pair<Relation,String>, HashSet<Way>> backlinks =
    89             new HashMap<Pair<Relation,String>, HashSet<Way>>();
    90         HashSet<Relation> relationsUsingWays = new HashSet<Relation>();
    91         for (Relation r : getCurrentDataSet().relations) {
    92             if (r.isDeleted() || r.incomplete) {
    93                 continue;
    94             }
    95             for (RelationMember rm : r.getMembers()) {
    96                 if (rm.isWay()) {
    97                     for(Way w : selectedWays) {
    98                         if (rm.getMember() == w) {
    99                             Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.getRole());
    100                             HashSet<Way> waylinks = new HashSet<Way>();
    101                             if (backlinks.containsKey(pair)) {
    102                                 waylinks = backlinks.get(pair);
    103                             } else {
    104                                 waylinks = new HashSet<Way>();
    105                                 backlinks.put(pair, waylinks);
    106                             }
    107                             waylinks.add(w);
    108 
    109                             // this is just a cache for later use
    110                             relationsUsingWays.add(r);
    111                         }
    112                     }
    113                 }
    114             }
    115         }
    116 
    117         // Complain to the user if the ways don't have equal memberships.
    118         for (HashSet<Way> waylinks : backlinks.values()) {
    119             if (!waylinks.containsAll(selectedWays)) {
    120 
    121                 ExtendedDialog ed = new ExtendedDialog(Main.parent,
    122                         tr("Combine ways with different memberships?"),
    123                         new String[] {tr("Combine Anyway"), tr("Cancel")});
    124                 ed.setButtonIcons(new String[] {"combineway.png", "cancel.png"});
    125                 ed.setContent(tr("The selected ways have differing relation memberships.  "
    126                         + "Do you still want to combine them?"));
    127                 ed.showDialog();
    128 
    129                 if (ed.getValue() == 1) {
    130                     break;
    131                 }
    132 
    133                 return;
    134             }
    135         }
    136 
    137         // collect properties for later conflict resolving
    138         Map<String, Set<String>> props = new TreeMap<String, Set<String>>();
    139         for (Way w : selectedWays) {
    140             for (Entry<String,String> e : w.entrySet()) {
    141                 if (!props.containsKey(e.getKey())) {
    142                     props.put(e.getKey(), new TreeSet<String>());
    143                 }
    144                 props.get(e.getKey()).add(e.getValue());
    145             }
    146         }
    147 
    148         List<Node> nodeList = null;
    149         Object firstTry = actuallyCombineWays(selectedWays, false);
    150         if (firstTry instanceof List<?>) {
    151             nodeList = (List<Node>) firstTry;
    152         } else {
    153             Object secondTry = actuallyCombineWays(selectedWays, true);
    154             if (secondTry instanceof List<?>) {
    155                 ExtendedDialog ed = new ExtendedDialog(Main.parent,
    156                         tr("Change directions?"),
    157                         new String[] {tr("Reverse and Combine"), tr("Cancel")});
    158                 ed.setButtonIcons(new String[] {"wayflip.png", "cancel.png"});
    159                 ed.setContent(tr("The ways can not be combined in their current directions.  "
    160                         + "Do you want to reverse some of them?"));
    161                 ed.showDialog();
    162                 if (ed.getValue() != 1) return;
    163 
    164                 nodeList = (List<Node>) secondTry;
    165             } else {
    166                 JOptionPane.showMessageDialog(
    167                         Main.parent,
    168                         secondTry, // FIXME: not sure whether this fits in a dialog
    169                         tr("Information"),
    170                         JOptionPane.INFORMATION_MESSAGE
    171                 );
    172                 return;
    173             }
    174         }
    175 
    176         // Find the most appropriate way to modify.
    177 
    178         // Eventually this might want to be the way with the longest
    179         // history or the longest selected way but for now just attempt
    180         // to reuse an existing id.
    181         Way modifyWay = selectedWays.peek();
    182         for (Way w : selectedWays) {
    183             modifyWay = w;
    184             if (w.getId() != 0) {
    185                 break;
    186             }
    187         }
    188         Way newWay = new Way(modifyWay);
    189 
    190         newWay.setNodes(nodeList);
    191 
    192         // display conflict dialog
    193         Map<String, JComboBox> components = new HashMap<String, JComboBox>();
    194         JPanel p = new JPanel(new GridBagLayout());
    195         for (Entry<String, Set<String>> e : props.entrySet()) {
    196             if (TigerUtils.isTigerTag(e.getKey())) {
    197                 String combined = TigerUtils.combineTags(e.getKey(), e.getValue());
    198                 newWay.put(e.getKey(), combined);
    199             } else if (e.getValue().size() > 1) {
    200                 JComboBox c = new JComboBox(e.getValue().toArray());
    201                 c.setEditable(true);
    202                 p.add(new JLabel(e.getKey()), GBC.std());
    203                 p.add(Box.createHorizontalStrut(10), GBC.std());
    204                 p.add(c, GBC.eol());
    205                 components.put(e.getKey(), c);
    206             } else {
    207                 newWay.put(e.getKey(), e.getValue().iterator().next());
    208             }
    209         }
    210 
    211         if (!components.isEmpty()) {
    212 
    213             ExtendedDialog ed = new ExtendedDialog(Main.parent,
    214                     tr("Enter values for all conflicts."),
    215                     new String[] {tr("Solve Conflicts"), tr("Cancel")});
    216             ed.setButtonIcons(new String[] {"dialogs/conflict.png", "cancel.png"});
    217             ed.setContent(p);
    218             ed.showDialog();
    219 
    220             if (ed.getValue() != 1) return;
    221 
    222             for (Entry<String, JComboBox> e : components.entrySet()) {
    223                 newWay.put(e.getKey(), e.getValue().getEditor().getItem().toString());
    224             }
    225         }
    226 
    227         LinkedList<Command> cmds = new LinkedList<Command>();
    228         LinkedList<Way> deletedWays = new LinkedList<Way>(selectedWays);
    229         deletedWays.remove(modifyWay);
    230         cmds.add(new DeleteCommand(deletedWays));
    231         cmds.add(new ChangeCommand(modifyWay, newWay));
    232 
    233         // modify all relations containing the now-deleted ways
    234         for (Relation r : relationsUsingWays) {
    235             List<RelationMember> newMembers = new ArrayList<RelationMember>();
    236             HashSet<String> rolesToReAdd = new HashSet<String>();
    237             for (RelationMember rm : r.getMembers()) {
    238                 // Don't copy the member if it to one of our ways, just keep a
    239                 // note to re-add it later on.
    240                 if (selectedWays.contains(rm.getMember())) {
    241                     rolesToReAdd.add(rm.getRole());
    242                 } else {
    243                     newMembers.add(rm);
    244                 }
    245             }
    246             for (String role : rolesToReAdd) {
    247                 newMembers.add(new RelationMember(role, modifyWay));
    248             }
    249             Relation newRel = new Relation(r);
    250             newRel.setMembers(newMembers);
    251             cmds.add(new ChangeCommand(r, newRel));
    252         }
    253         Main.main.undoRedo.add(new SequenceCommand(tr("Combine {0} ways", selectedWays.size()), cmds));
    254         getCurrentDataSet().setSelected(modifyWay);
    255     }
    256 
    257     /**
    258      * @return a message if combining failed, else a list of nodes.
    259      */
    260     private Object actuallyCombineWays(List<Way> ways, boolean ignoreDirection) {
    261         // Battle plan:
    262         //  1. Split the ways into small chunks of 2 nodes and weed out
    263         //     duplicates.
    264         //  2. Take a chunk and see if others could be appended or prepended,
    265         //     if so, do it and remove it from the list of remaining chunks.
    266         //     Rather, rinse, repeat.
    267         //  3. If this algorithm does not produce a single way,
    268         //     complain to the user.
    269         //  4. Profit!
    270 
    271         HashSet<Pair<Node,Node>> chunkSet = new HashSet<Pair<Node,Node>>();
    272         for (Way w : ways) {
    273             chunkSet.addAll(w.getNodePairs(ignoreDirection));
    274         }
    275 
    276         LinkedList<Pair<Node,Node>> chunks = new LinkedList<Pair<Node,Node>>(chunkSet);
    277 
    278         if (chunks.isEmpty())
    279             return tr("All the ways were empty");
    280 
    281         List<Node> nodeList = Pair.toArrayList(chunks.poll());
    282         while (!chunks.isEmpty()) {
    283             ListIterator<Pair<Node,Node>> it = chunks.listIterator();
    284             boolean foundChunk = false;
    285             while (it.hasNext()) {
    286                 Pair<Node,Node> curChunk = it.next();
    287                 if (curChunk.a == nodeList.get(nodeList.size() - 1)) { // append
    288                     nodeList.add(curChunk.b);
    289                 } else if (curChunk.b == nodeList.get(0)) { // prepend
    290                     nodeList.add(0, curChunk.a);
    291                 } else if (ignoreDirection && curChunk.b == nodeList.get(nodeList.size() - 1)) { // append
    292                     nodeList.add(curChunk.a);
    293                 } else if (ignoreDirection && curChunk.a == nodeList.get(0)) { // prepend
    294                     nodeList.add(0, curChunk.b);
    295                 } else {
    296                     continue;
    297                 }
    298 
    299                 foundChunk = true;
    300                 it.remove();
    301                 break;
    302             }
    303             if (!foundChunk) {
    304                 break;
    305             }
    306         }
    307 
    308         if (!chunks.isEmpty())
    309             return tr("Could not combine ways "
    310                     + "(They could not be merged into a single string of nodes)");
    311 
    312         return nodeList;
     223        combineWays(selectedWays);
    313224    }
    314225
     
    328239        setEnabled(numWays >= 2);
    329240    }
     241
     242    /**
     243     * This is a collection of relations referring to at least one out of a set of
     244     * ways.
     245     *
     246     *
     247     */
     248    static private class WayReferringRelations {
     249        /**
     250         * the map references between relations and ways. The key is a ways, the value is a
     251         * set of relations referring to that way.
     252         */
     253        private Map<Way, Set<Relation>> wayRelationMap;
     254
     255        /**
     256         *
     257         * @param ways  a collection of ways
     258         */
     259        public WayReferringRelations(Collection<Way> ways) {
     260            wayRelationMap = new HashMap<Way, Set<Relation>>();
     261            if (ways == null) return;
     262            ways.remove(null); // just in case - remove null values
     263            for (Way way: ways) {
     264                if (!wayRelationMap.containsKey(way)) {
     265                    wayRelationMap.put(way, new HashSet<Relation>());
     266                }
     267            }
     268        }
     269
     270        /**
     271         * build the sets of referring relations from the relations in the dataset <code>ds</code>
     272         *
     273         * @param ds the data set
     274         */
     275        public void build(DataSet ds) {
     276            for (Relation r: ds.relations) {
     277                if (r.isDeleted() || r.incomplete) {
     278                    continue;
     279                }
     280                Set<Way> referringWays = OsmPrimitive.getFilteredSet(r.getMemberPrimitives(), Way.class);
     281                for (Way w : wayRelationMap.keySet()) {
     282                    if (referringWays.contains(w)) {
     283                        wayRelationMap.get(w).add(r);
     284                    }
     285                }
     286            }
     287        }
     288
     289        /**
     290         * Replies the ways
     291         * @return the ways
     292         */
     293        public Set<Way> getWays() {
     294            return wayRelationMap.keySet();
     295        }
     296
     297        /**
     298         * Replies the set of referring relations
     299         *
     300         * @return the set of referring relations
     301         */
     302        public Set<Relation> getRelations() {
     303            HashSet<Relation> ret = new HashSet<Relation>();
     304            for (Way w: wayRelationMap.keySet()) {
     305                ret.addAll(wayRelationMap.get(w));
     306            }
     307            return ret;
     308        }
     309
     310        /**
     311         * Replies the set of referring relations for a specific way
     312         *
     313         * @return the set of referring relations
     314         */
     315        public Set<Relation> getRelations(Way way) {
     316            return wayRelationMap.get(way);
     317        }
     318
     319        protected Command buildRelationUpdateCommand(Relation relation, Collection<Way> ways, Way targetWay) {
     320            List<RelationMember> newMembers = new ArrayList<RelationMember>();
     321            for (RelationMember rm : relation.getMembers()) {
     322                if (ways.contains(rm.getMember())) {
     323                    RelationMember newMember = new RelationMember(rm.getRole(),targetWay);
     324                    newMembers.add(newMember);
     325                } else {
     326                    newMembers.add(rm);
     327                }
     328            }
     329            Relation newRelation = new Relation(relation);
     330            newRelation.setMembers(newMembers);
     331            return new ChangeCommand(relation, newRelation);
     332        }
     333
     334        public List<Command> buildRelationUpdateCommands(Way targetWay) {
     335            Collection<Way> toRemove = getWays();
     336            toRemove.remove(targetWay);
     337            ArrayList<Command> cmds = new ArrayList<Command>();
     338            for (Relation r : getRelations()) {
     339                Command cmd = buildRelationUpdateCommand(r, toRemove, targetWay);
     340                cmds.add(cmd);
     341            }
     342            return cmds;
     343        }
     344    }
     345
     346    static public class NodePair {
     347        private Node a;
     348        private Node b;
     349        public NodePair(Node a, Node b) {
     350            this.a =a;
     351            this.b = b;
     352        }
     353
     354        public NodePair(Pair<Node,Node> pair) {
     355            this.a = pair.a;
     356            this.b = pair.b;
     357        }
     358
     359        public NodePair(NodePair other) {
     360            this.a = other.a;
     361            this.b = other.b;
     362        }
     363
     364        public Node getA() {
     365            return a;
     366        }
     367
     368        public Node getB() {
     369            return b;
     370        }
     371
     372        public boolean isAdjacentToA(NodePair other) {
     373            return other.getA() == a || other.getB() == a;
     374        }
     375
     376        public boolean isAdjacentToB(NodePair other) {
     377            return other.getA() == b || other.getB() == b;
     378        }
     379
     380        public boolean isSuccessorOf(NodePair other) {
     381            return other.getB() == a;
     382        }
     383
     384        public boolean isPredecessorOf(NodePair other) {
     385            return b == other.getA();
     386        }
     387
     388        public NodePair swap() {
     389            return new NodePair(b,a);
     390        }
     391
     392        @Override
     393        public String toString() {
     394            return new StringBuilder()
     395            .append("[")
     396            .append(a.getId())
     397            .append(",")
     398            .append(b.getId())
     399            .append("]")
     400            .toString();
     401        }
     402
     403        public boolean contains(Node n) {
     404            return a == n || b == n;
     405        }
     406
     407        @Override
     408        public int hashCode() {
     409            final int prime = 31;
     410            int result = 1;
     411            result = prime * result + ((a == null) ? 0 : a.hashCode());
     412            result = prime * result + ((b == null) ? 0 : b.hashCode());
     413            return result;
     414        }
     415        @Override
     416        public boolean equals(Object obj) {
     417            if (this == obj)
     418                return true;
     419            if (obj == null)
     420                return false;
     421            if (getClass() != obj.getClass())
     422                return false;
     423            NodePair other = (NodePair) obj;
     424            if (a == null) {
     425                if (other.a != null)
     426                    return false;
     427            } else if (!a.equals(other.a))
     428                return false;
     429            if (b == null) {
     430                if (other.b != null)
     431                    return false;
     432            } else if (!b.equals(other.b))
     433                return false;
     434            return true;
     435        }
     436    }
     437
     438
     439    static public class NodeGraph {
     440        static public List<NodePair> buildNodePairs(Way way, boolean directed) {
     441            ArrayList<NodePair> pairs = new ArrayList<NodePair>();
     442            for (Pair<Node,Node> pair: way.getNodePairs(false /* don't sort */)) {
     443                pairs.add(new NodePair(pair));
     444                if (!directed) {
     445                    pairs.add(new NodePair(pair).swap());
     446                }
     447            }
     448            return pairs;
     449        }
     450
     451        static public List<NodePair> buildNodePairs(List<Way> ways, boolean directed) {
     452            ArrayList<NodePair> pairs = new ArrayList<NodePair>();
     453            for (Way w: ways) {
     454                pairs.addAll(buildNodePairs(w, directed));
     455            }
     456            return pairs;
     457        }
     458
     459        static public List<NodePair> eliminateDuplicateNodePairs(List<NodePair> pairs) {
     460            ArrayList<NodePair> cleaned = new ArrayList<NodePair>();
     461            for(NodePair p: pairs) {
     462                if (!cleaned.contains(p) && !cleaned.contains(p.swap())) {
     463                    cleaned.add(p);
     464                }
     465            }
     466            return cleaned;
     467        }
     468
     469        static public NodeGraph createDirectedGraphFromNodePairs(List<NodePair> pairs) {
     470            NodeGraph graph = new NodeGraph();
     471            for (NodePair pair: pairs) {
     472                graph.add(pair);
     473            }
     474            return graph;
     475        }
     476
     477        static public NodeGraph createDirectedGraphFromWays(Collection<Way> ways) {
     478            NodeGraph graph = new NodeGraph();
     479            for (Way w: ways) {
     480                graph.add(buildNodePairs(w, true /* directed */));
     481            }
     482            return graph;
     483        }
     484
     485        static public NodeGraph createUndirectedGraphFromNodeList(List<NodePair> pairs) {
     486            NodeGraph graph = new NodeGraph();
     487            for (NodePair pair: pairs) {
     488                graph.add(pair);
     489                graph.add(pair.swap());
     490            }
     491            return graph;
     492        }
     493
     494        static public NodeGraph createUndirectedGraphFromNodeWays(Collection<Way> ways) {
     495            NodeGraph graph = new NodeGraph();
     496            for (Way w: ways) {
     497                graph.add(buildNodePairs(w, false /* undirected */));
     498            }
     499            return graph;
     500        }
     501
     502        private Set<NodePair> edges;
     503        private int numUndirectedEges = 0;
     504
     505        protected void computeNumEdges() {
     506            Set<NodePair> undirectedEdges = new HashSet<NodePair>();
     507            for (NodePair pair: edges) {
     508                if (!undirectedEdges.contains(pair) && ! undirectedEdges.contains(pair.swap())) {
     509                    undirectedEdges.add(pair);
     510                }
     511            }
     512            numUndirectedEges = undirectedEdges.size();
     513        }
     514
     515        public NodeGraph() {
     516            edges = new HashSet<NodePair>();
     517        }
     518
     519        public void add(NodePair pair) {
     520            if (!edges.contains(pair)) {
     521                edges.add(pair);
     522            }
     523        }
     524
     525        public void add(List<NodePair> pairs) {
     526            for (NodePair pair: pairs) {
     527                add(pair);
     528            }
     529        }
     530
     531        protected Node getStartNode() {
     532            return edges.iterator().next().getA();
     533        }
     534
     535        protected Set<Node> getNodes(Stack<NodePair> pairs) {
     536            HashSet<Node> nodes = new HashSet<Node>();
     537            for (NodePair pair: pairs) {
     538                nodes.add(pair.getA());
     539                nodes.add(pair.getB());
     540            }
     541            return nodes;
     542        }
     543
     544        protected List<NodePair> getOutboundPairs(NodePair pair) {
     545            LinkedList<NodePair> outbound = new LinkedList<NodePair>();
     546            for (NodePair candidate:edges) {
     547                if (candidate.equals(pair)) {
     548                    continue;
     549                }
     550                if (candidate.isSuccessorOf(pair)) {
     551                    outbound.add(candidate);
     552                }
     553            }
     554            return outbound;
     555        }
     556
     557        protected List<NodePair> getOutboundPairs(Node node) {
     558            LinkedList<NodePair> outbound = new LinkedList<NodePair>();
     559            for (NodePair candidate:edges) {
     560                if (candidate.getA() == node) {
     561                    outbound.add(candidate);
     562                }
     563            }
     564            return outbound;
     565        }
     566
     567        protected Set<Node> getNodes() {
     568            Set<Node> nodes = new HashSet<Node>();
     569            for (NodePair pair: edges) {
     570                nodes.add(pair.getA());
     571                nodes.add(pair.getB());
     572            }
     573            return nodes;
     574        }
     575
     576        protected boolean isSpanningWay(Stack<NodePair> way) {
     577            return numUndirectedEges == way.size();
     578        }
     579
     580
     581        protected boolean advance(Stack<NodePair> path) {
     582            // found a spanning path ?
     583            //
     584            if (isSpanningWay(path))
     585                return true;
     586
     587            // advance with one of the possible follow up nodes
     588            //
     589            Stack<NodePair> nextPairs = new Stack<NodePair>();
     590            nextPairs.addAll(getOutboundPairs(path.peek()));
     591            while(!nextPairs.isEmpty()) {
     592                NodePair next = nextPairs.pop();
     593                if (path.contains(next) || path.contains(next.swap())) {
     594                    continue;
     595                }
     596                path.push(next);
     597                if (advance(path)) return true;
     598                path.pop();
     599            }
     600            return false;
     601        }
     602
     603        protected List<Node> buildPathFromNodePairs(Stack<NodePair> path) {
     604            LinkedList<Node> ret = new LinkedList<Node>();
     605            for (NodePair pair: path) {
     606                ret.add(pair.getA());
     607            }
     608            ret.add(path.peek().getB());
     609            return ret;
     610        }
     611
     612        protected List<Node> buildSpanningPath(Node startNode) {
     613            if (startNode == null)
     614                return null;
     615            Stack<NodePair> path = new Stack<NodePair>();
     616            // advance with one of the possible follow up nodes
     617            //
     618            Stack<NodePair> nextPairs  = new Stack<NodePair>();
     619            nextPairs.addAll(getOutboundPairs(startNode));
     620            while(!nextPairs.isEmpty()) {
     621                path.push(nextPairs.pop());
     622                if (advance(path))
     623                    return buildPathFromNodePairs(path);
     624                path.pop();
     625            }
     626            return null;
     627        }
     628
     629        public List<Node> buildSpanningPath() {
     630            computeNumEdges();
     631            for (Node n : getNodes()) {
     632                List<Node> path = buildSpanningPath(n);
     633                if (path != null)
     634                    return path;
     635            }
     636            return null;
     637        }
     638    }
    330639}
  • trunk/src/org/openstreetmap/josm/actions/CopyAction.java

    r2008 r2070  
    101101                    members.add(mnew);
    102102                }
    103                 enew.members.addAll(members);
     103                enew.setMembers(members);
    104104                pasteBuffer.addPrimitive(enew);
    105105            }
  • trunk/src/org/openstreetmap/josm/actions/DiskAccessAction.java

    r2029 r2070  
    5050        if (!open) {
    5151            File file = fc.getSelectedFile();
    52             if (file == null || (file.exists() && 1 !=
    53                 new ExtendedDialog(Main.parent,
     52            if (file != null && file.exists()) {
     53                ExtendedDialog dialog = new ExtendedDialog(
     54                        Main.parent,
    5455                        tr("Overwrite"),
    55                         tr("File exists. Overwrite?"),
    56                         new String[] {tr("Overwrite"), tr("Cancel")},
    57                         new String[] {"save_as.png", "cancel.png"}).getValue()))
    58                 return null;
     56                        new String[] {tr("Overwrite"), tr("Cancel")}
     57                );
     58                dialog.setContent(tr("File exists. Overwrite?"));
     59                dialog.setButtonIcons(new String[] {"save_as.png", "cancel.png"});
     60                if (dialog.getValue() != 1)
     61                    return null;
     62            }
    5963        }
    6064
  • trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java

    r2064 r2070  
    201201
    202202        if (!components.isEmpty()) {
    203             int answer = new ExtendedDialog(Main.parent,
     203            ExtendedDialog dialog = new ExtendedDialog(
     204                    Main.parent,
    204205                    tr("Enter values for all conflicts."),
    205                     p,
    206                     new String[] {tr("Solve Conflicts"), tr("Cancel")},
    207                     new String[] {"dialogs/conflict.png", "cancel.png"}).getValue();
     206                    new String[] {tr("Solve Conflicts"), tr("Cancel")}
     207            );
     208            dialog.setButtonIcons(new String[] {"dialogs/conflict.png", "cancel.png"});
     209            dialog.setContent(p);
     210            dialog.showDialog();
     211            int answer = dialog.getValue();
     212
    208213            if (answer != 1)
    209214                return null;
  • trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java

    r2047 r2070  
    1717import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    1818import org.openstreetmap.josm.io.FileImporter;
     19import org.openstreetmap.josm.io.IllegalDataException;
    1920import org.openstreetmap.josm.io.OsmTransferException;
    2021import org.openstreetmap.josm.tools.Shortcut;
     
    5152    }
    5253
    53     static public void openFile(File f) throws IOException {
     54    static public void openFile(File f) throws IOException, IllegalDataException {
    5455        for (FileImporter importer : ExtensionFileFilter.importers)
    5556            if (importer.acceptFile(f)) {
  • trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java

    r1677 r2070  
    3131    public OpenLocationAction() {
    3232        super(tr("Open Location..."), "openlocation", tr("Open an URL."),
    33         Shortcut.registerShortcut("system:open_location", tr("File: {0}", tr("Open Location...")), KeyEvent.VK_L, Shortcut.GROUP_MENU), true);
     33                Shortcut.registerShortcut("system:open_location", tr("File: {0}", tr("Open Location...")), KeyEvent.VK_L, Shortcut.GROUP_MENU), true);
    3434    }
    3535
     
    4343        all.add(urltext, GBC.eol());
    4444        all.add(layer, GBC.eol());
    45         int answer = new ExtendedDialog(Main.parent,
    46                         tr("Download Location"),
    47                         all,
    48                         new String[] {tr("Download URL"), tr("Cancel")},
    49                         new String[] {"download.png", "cancel.png"}).getValue();
    50         if (answer != 1) return;
     45        ExtendedDialog dialog = new ExtendedDialog(Main.parent,
     46                tr("Download Location"),
     47                new String[] {tr("Download URL"), tr("Cancel")}
     48        );
     49        dialog.setContent(all);
     50        dialog.setButtonIcons(new String[] {"download.png", "cancel.png"});
     51        if (dialog.getValue() != 1) return;
    5152        openUrl(layer.isSelected(), urltext.getText());
    5253    }
  • trunk/src/org/openstreetmap/josm/actions/PasteAction.java

    r1938 r2070  
    6969        for (Node n : pasteBuffer.nodes) {
    7070            Node nnew = new Node(n);
    71             nnew.id = 0;
     71            nnew.clearOsmId();
    7272            if (Main.map.mapView.getEditLayer() == source) {
    7373                nnew.setEastNorth(nnew.getEastNorth().add(offsetEast, offsetNorth));
     
    7878            Way wnew = new Way();
    7979            wnew.cloneFrom(w);
    80             wnew.id = 0;
     80            wnew.clearOsmId();
    8181            /* make sure we reference the new nodes corresponding to the old ones */
    8282            List<Node> nodes = new ArrayList<Node>();
     
    8989        for (Relation r : pasteBuffer.relations) {
    9090            Relation rnew = new Relation(r);
    91             rnew.id = 0;
     91            r.clearOsmId();
    9292            List<RelationMember> members = new ArrayList<RelationMember>();
    9393            for (RelationMember m : r.getMembers()) {
    9494                OsmPrimitive mo = map.get(m.getMember());
    95                 if(mo != null) /* TODO - This only prevents illegal data, but kills the relation */
     95                if(mo != null) /* FIXME - This only prevents illegal data, but kills the relation */
    9696                {
    97                     RelationMember mnew = new RelationMember(m);
    98                     mnew.member = map.get(m.getMember());
     97                    RelationMember mnew = new RelationMember(m.getRole(), map.get(m.getMember()));
    9998                    members.add(mnew);
    10099                }
  • trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java

    r2025 r2070  
    3737    }
    3838
     39    static private List<Class<? extends OsmPrimitive>> osmPrimitiveClasses;
     40    {
     41        osmPrimitiveClasses = new ArrayList<Class<? extends OsmPrimitive>>();
     42        osmPrimitiveClasses.add(Node.class);
     43        osmPrimitiveClasses.add(Way.class);
     44        osmPrimitiveClasses.add(Relation.class);
     45    }
     46
    3947    /**
    4048     * Replies true if the source for tag pasting is heterogeneous, i.e. if it doesn't consist of
     
    126134    protected Map<OsmPrimitiveType, Integer> getSourceStatistics() {
    127135        HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
    128         for (Class<? extends OsmPrimitive> type: new Class[] {Node.class, Way.class, Relation.class}) {
     136        for (Class<? extends OsmPrimitive> type: osmPrimitiveClasses) {
    129137            if (!getSourceTagsByType(type).isEmpty()) {
    130138                ret.put(OsmPrimitiveType.from(type), getSourcePrimitivesByType(type).size());
     
    136144    protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
    137145        HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
    138         for (Class<? extends OsmPrimitive> type: new Class[] {Node.class, Way.class, Relation.class}) {
     146        for (Class<? extends OsmPrimitive> type: osmPrimitiveClasses) {
    139147            int count = getSubcollectionByType(getEditLayer().data.getSelected(), type).size();
    140148            if (count > 0) {
     
    156164    protected void pasteFromHomogeneousSource(Collection<? extends OsmPrimitive> targets) {
    157165        TagCollection tc = null;
    158         for (Class<? extends OsmPrimitive> type : new Class[] {Node.class, Way.class, Relation.class}) {
     166        for (Class<? extends OsmPrimitive> type : osmPrimitiveClasses) {
    159167            TagCollection tc1 = getSourceTagsByType(type);
    160168            if (!tc1.isEmpty()) {
  • trunk/src/org/openstreetmap/josm/actions/SaveAction.java

    r2017 r2070  
    3434            f=null;
    3535        }
    36         if(f != null && layer instanceof GpxLayer && 1 !=
    37             new ExtendedDialog(Main.parent, tr("Overwrite"),
    38                     tr("File {0} exists. Overwrite?", f.getName()),
    39                     new String[] {tr("Overwrite"), tr("Cancel")},
    40                     new String[] {"save_as.png", "cancel.png"}).getValue()) {
    41             f = null;
     36
     37        // FIXME: why only for GpxLayer?
     38        if(f != null && layer instanceof GpxLayer) {
     39            ExtendedDialog dialog = new ExtendedDialog(
     40                    Main.parent,
     41                    tr("Overwrite"),
     42                    new String[] {tr("Overwrite"), tr("Cancel")}
     43            );
     44            dialog.setButtonIcons(new String[] {"save_as.png", "cancel.png"});
     45            dialog.setContent(tr("File {0} exists. Overwrite?", f.getName()));
     46            dialog.showDialog();
     47            int ret = dialog.getValue();
     48            if (ret != 1) {
     49                f = null;
     50            }
    4251        }
    4352        return f == null ? openFileDialog(layer) : f;
  • trunk/src/org/openstreetmap/josm/actions/SaveActionBase.java

    r2029 r2070  
    9191     */
    9292    public boolean checkSaveConditions(Layer layer) {
    93         if (layer instanceof OsmDataLayer && isDataSetEmpty((OsmDataLayer)layer) && 1 != new ExtendedDialog(Main.parent, tr("Empty document"), tr("The document contains no data."), new String[] {tr("Save anyway"), tr("Cancel")}, new String[] {"save.png", "cancel.png"}).getValue())
    94             return false;
     93        if (layer instanceof OsmDataLayer && isDataSetEmpty((OsmDataLayer)layer)) {
     94            ExtendedDialog dialog = new ExtendedDialog(
     95                    Main.parent,
     96                    tr("Empty document"),
     97                    new String[] {tr("Save anyway"), tr("Cancel")}
     98            );
     99            dialog.setContent(tr("The document contains no data."));
     100            dialog.setButtonIcons(new String[] {"save.png", "cancel.png"});
     101            dialog.showDialog();
     102            if (dialog.getValue() != 1) return false;
     103        }
     104
    95105        if (layer instanceof GpxLayer && ((GpxLayer)layer).data == null)
    96106            return false;
     
    98108            ConflictCollection conflicts = ((OsmDataLayer)layer).getConflicts();
    99109            if (conflicts != null && !conflicts.isEmpty()) {
    100                 int answer = new ExtendedDialog(Main.parent,
     110                ExtendedDialog dialog = new ExtendedDialog(
     111                        Main.parent,
    101112                        tr("Conflicts"),
    102                         tr("There are unresolved conflicts. Conflicts will not be saved and handled as if you rejected all. Continue?"),
    103                         new String[] {tr("Reject Conflicts and Save"), tr("Cancel")},
    104                         new String[] {"save.png", "cancel.png"}).getValue();
    105 
    106                 if (answer != 1) return false;
     113                        new String[] {tr("Reject Conflicts and Save"), tr("Cancel")}
     114                );
     115                dialog.setContent(tr("There are unresolved conflicts. Conflicts will not be saved and handled as if you rejected all. Continue?"));
     116                dialog.setButtonIcons(new String[] {"save.png", "cancel.png"});
     117                dialog.showDialog();
     118                if (dialog.getValue() != 1) return false;
    107119            }
    108120        }
     
    190202            }
    191203        }
    192         if(file == null || (file.exists() && 1 != new ExtendedDialog(Main.parent,
    193                 tr("Overwrite"), tr("File exists. Overwrite?"),
    194                 new String[] {tr("Overwrite"), tr("Cancel")},
    195                 new String[] {"save_as.png", "cancel.png"}).getValue()))
    196             return null;
     204        if(file == null || (file.exists())) {
     205            ExtendedDialog dialog = new ExtendedDialog(
     206                    Main.parent,
     207                    tr("Overwrite"),
     208                    new String[] {tr("Overwrite"), tr("Cancel")}
     209            );
     210            dialog.setContent(tr("File exists. Overwrite?"));
     211            dialog.setButtonIcons(new String[] {"save_as.png", "cancel.png"});
     212            dialog.showDialog();
     213            if (dialog.getValue() != 1) return null;
     214        }
    197215        return file;
    198216    }
  • trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java

    r2025 r2070  
    156156
    157157        Node n = new Node(selectedNode);
    158         n.id = 0;
     158        n.clearOsmId();
    159159
    160160        // If this wasn't called from menu, place it where the cursor is/was
     
    301301                // clone the node for all other ways
    302302                pushNode = new Node(pushNode);
    303                 pushNode.id = 0;
     303                pushNode.clearOsmId();
    304304                newNodes.add(pushNode);
    305305                cmds.add(new AddCommand(pushNode));
     
    331331                        if (newRel == null) {
    332332                            newRel = new Relation(r);
    333                             newRel.members.clear();
     333                            newRel.setMembers(null);
    334334                            rolesToReAdd = new HashSet<String>();
    335335                        }
     
    340340            if (newRel != null) {
    341341                for (RelationMember rm : r.getMembers()) {
    342                     //if (rm.member != selectedNode) {
    343                     newRel.members.add(rm);
    344                     //}
     342                    newRel.addMember(rm);
    345343                }
    346344                for (Node n : newNodes) {
    347345                    for (String role : rolesToReAdd) {
    348                         newRel.members.add(new RelationMember(role, n));
     346                        newRel.addMember(new RelationMember(role, n));
    349347                    }
    350348                }
  • trunk/src/org/openstreetmap/josm/actions/search/SearchAction.java

    r2068 r2070  
    118118        p.add(left);
    119119        p.add(right);
    120 
    121         int result = new ExtendedDialog(Main.parent,
     120        ExtendedDialog dialog = new ExtendedDialog(
     121                Main.parent,
    122122                tr("Search"),
    123                 p,
    124                 new String[] {tr("Start Search"), tr("Cancel")},
    125                 new String[] {"dialogs/search.png", "cancel.png"}).getValue();
     123                new String[] {tr("Start Search"), tr("Cancel")}
     124        );
     125        dialog.setButtonIcons(new String[] {"dialogs/search.png", "cancel.png"});
     126        dialog.setContent(p);
     127        dialog.showDialog();
     128        int result = dialog.getValue();
     129
    126130        if(result != 1) return;
    127131
  • trunk/src/org/openstreetmap/josm/actions/search/SearchCompiler.java

    r2025 r2070  
    77import java.io.PushbackReader;
    88import java.io.StringReader;
     9import java.util.List;
    910import java.util.Map.Entry;
    1011import java.util.regex.Matcher;
     
    8283        public Id(long id) {this.id = id;}
    8384        @Override public boolean match(OsmPrimitive osm) {
    84             return osm.id == id;
     85            return osm.getId() == id;
    8586        }
    8687        @Override public String toString() {return "id="+id;}
     
    329330            }
    330331            if (osm.user != null) {
    331                 String name = osm.user.name;
     332                String name = osm.user.getName();
    332333                // is not Java 1.5
    333334                //String name = java.text.Normalizer.normalize(name, java.text.Normalizer.Form.NFC);
     
    364365    private static class UserMatch extends Match {
    365366        private User user;
    366         public UserMatch(String user) { this.user = User.get(user); }
    367         @Override public boolean match(OsmPrimitive osm) {
    368             return osm.user == user;
    369         }
    370         @Override public String toString() { return "user=" + user.name; }
     367        public UserMatch(String user) {
     368            List<User> users = User.getByName(user);
     369            if (!users.isEmpty()) {
     370                // selecting an arbitrary user
     371                this.user = users.get(0);
     372            } else {
     373                user = null;
     374            }
     375        }
     376        @Override public boolean match(OsmPrimitive osm) {
     377            if (osm.user == null && user == null) return true;
     378            if (osm.user == null) return false;
     379            return osm.user.equals(user);
     380        }
     381        @Override public String toString() {
     382            return "user=" + user == null ? "" : user.getName();
     383        }
    371384    }
    372385
  • trunk/src/org/openstreetmap/josm/command/ChangeRelationMemberRoleCommand.java

    r2025 r2070  
    4949
    5050        oldRole = relation.getMember(position).getRole();
    51         relation.getMember(position).role = newRole;
     51        relation.getMember(position).getRole().equals(newRole);
    5252
    5353        oldModified = relation.isModified();
     
    5757
    5858    @Override public void undoCommand() {
    59         relation.getMember(position).role = oldRole;
     59        relation.getMember(position).getRole().equals(oldRole);
    6060        relation.setModified(oldModified);
    6161    }
  • trunk/src/org/openstreetmap/josm/command/DeleteCommand.java

    r2054 r2070  
    9292        super.executeCommand();
    9393        for (OsmPrimitive osm : toDelete) {
    94             osm.delete(true);
     94            osm.setDeleted(true);
    9595        }
    9696        return true;
  • trunk/src/org/openstreetmap/josm/command/DeletedStateConflictResolveCommand.java

    r2039 r2070  
    7171            if (conflict.getTheir().isDeleted()) {
    7272                layer.data.unlinkReferencesToPrimitive(conflict.getMy());
    73                 conflict.getMy().delete(true);
     73                conflict.getMy().setDeleted(true);
    7474            } else {
    75                 conflict.getMy().delete(conflict.getTheir().isDeleted());
     75                conflict.getMy().setDeleted(conflict.getTheir().isDeleted());
    7676            }
    7777        } else
  • trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java

    r1938 r2070  
    185185            if (pair.getParent() instanceof Way) {
    186186                Way w = (Way)pair.getParent();
    187                 System.out.println(tr("removing reference from way {0}",w.id));
     187                System.out.println(tr("removing reference from way {0}",w.getId()));
    188188                List<Node> wayNodes = w.getNodes();
    189189                wayNodes.remove(primitive);
     
    194194                if (w.getNodesCount() < 2) {
    195195                    System.out.println(tr("Warning: Purging way {0} because number of nodes dropped below 2. Current is {1}",
    196                             w.id,w.getNodesCount()));
     196                            w.getId(),w.getNodesCount()));
    197197                    if (!hive.contains(w)) {
    198198                        hive.add(w);
     
    201201            } else if (pair.getParent() instanceof Relation) {
    202202                Relation r = (Relation)pair.getParent();
    203                 System.out.println(tr("removing reference from relation {0}",r.id));
     203                System.out.println(tr("removing reference from relation {0}",r.getId()));
    204204                r.removeMembersFor(primitive);
    205205            }
  • trunk/src/org/openstreetmap/josm/command/RelationMemberConflictResolverCommand.java

    r1951 r2070  
    5656        return new DefaultMutableTreeNode(
    5757                new JLabel(
    58                         tr("Resolve conflicts in member list of relation {0}", my.id),
     58                        tr("Resolve conflicts in member list of relation {0}", my.getId()),
    5959                        ImageProvider.get("data", "object"),
    6060                        JLabel.HORIZONTAL
  • trunk/src/org/openstreetmap/josm/command/RemoveRelationMemberCommand.java

    r1990 r2070  
    5151        } else {
    5252            relation.removeMember(removeIndex);
    53             relation.modified = true;
     53            relation.setModified(true);
    5454            return true;
    5555        }
     
    5959        super.undoCommand();
    6060        relation.addMember(member);
    61         relation.modified = this.getOrig(relation).modified;
     61        relation.setModified(this.getOrig(relation).isModified());
    6262    }
    6363
     
    6666    @Override public MutableTreeNode description() {
    6767        String msg = "";
    68         switch(OsmPrimitiveType.from(member.member)) {
    69         case NODE: msg = marktr("Remove node ''{0}'' at position {1} from relation ''{2}''"); break;
    70         case WAY: msg = marktr("Remove way ''{0}'' at position {1} from relation ''{2}''"); break;
    71         case RELATION: msg = marktr("Remove relation ''{0}'' at position {1} from relation ''{2}''"); break;
     68        switch(OsmPrimitiveType.from(member.getMember())) {
     69            case NODE: msg = marktr("Remove node ''{0}'' at position {1} from relation ''{2}''"); break;
     70            case WAY: msg = marktr("Remove way ''{0}'' at position {1} from relation ''{2}''"); break;
     71            case RELATION: msg = marktr("Remove relation ''{0}'' at position {1} from relation ''{2}''"); break;
    7272        }
    7373        return new DefaultMutableTreeNode(
    7474                new JLabel(
    7575                        tr(msg,
    76                                 member.member.getDisplayName(DefaultNameFormatter.getInstance()),
     76                                member.getMember().getDisplayName(DefaultNameFormatter.getInstance()),
    7777                                relation.getMembers().indexOf(member),
    7878                                relation.getDisplayName(DefaultNameFormatter.getInstance())
  • trunk/src/org/openstreetmap/josm/command/RotateCommand.java

    r2017 r2070  
    7676            os.latlon = new LatLon(n.getCoor());
    7777            os.eastNorth = n.getEastNorth();
    78             os.modified = n.modified;
     78            os.modified = n.isModified();
    7979            oldState.put(n, os);
    8080            pivot = pivot.add(os.eastNorth.east(), os.eastNorth.north());
     
    114114            n.setEastNorth(new EastNorth(nx, ny));
    115115            if (setModified) {
    116                 n.modified = true;
     116                n.setModified(true);
    117117            }
    118118        }
     
    128128            OldState os = oldState.get(n);
    129129            n.setCoor(os.latlon);
    130             n.modified = os.modified;
     130            n.setModified(os.modified);
    131131        }
    132132    }
  • trunk/src/org/openstreetmap/josm/command/TagConflictResolveCommand.java

    r2017 r2070  
    6767        String msg = "";
    6868        switch(OsmPrimitiveType.from(conflict.getMy())) {
    69         case NODE: msg = marktr("Resolve {0} tag conflicts in node {1}"); break;
    70         case WAY: msg = marktr("Resolve {0} tag conflicts in way {1}"); break;
    71         case RELATION: msg = marktr("Resolve {0} tag conflicts in relation {1}"); break;
     69            case NODE: msg = marktr("Resolve {0} tag conflicts in node {1}"); break;
     70            case WAY: msg = marktr("Resolve {0} tag conflicts in way {1}"); break;
     71            case RELATION: msg = marktr("Resolve {0} tag conflicts in relation {1}"); break;
    7272        }
    7373        return new DefaultMutableTreeNode(
    7474                new JLabel(
    75                         tr(msg,getNumDecidedConflicts(), conflict.getMy().id),
     75                        tr(msg,getNumDecidedConflicts(), conflict.getMy().getId()),
    7676                        ImageProvider.get("data", "object"),
    7777                        JLabel.HORIZONTAL
  • trunk/src/org/openstreetmap/josm/command/UndeletePrimitivesCommand.java

    r2017 r2070  
    8080                getLayer().getConflicts().remove(primitive);
    8181            }
    82             primitive.id = 0;
     82            primitive.clearOsmId();
    8383        }
    8484        return true;
  • trunk/src/org/openstreetmap/josm/command/VersionConflictResolveCommand.java

    r2017 r2070  
    3939        String msg = "";
    4040        switch(OsmPrimitiveType.from(conflict.getMy())) {
    41         case NODE: msg = marktr("Resolve version conflicts for node {0}"); break;
    42         case WAY: msg = marktr("Resolve version conflicts for way {0}"); break;
    43         case RELATION: msg = marktr("Resolve version conflicts for relation {0}"); break;
     41            case NODE: msg = marktr("Resolve version conflicts for node {0}"); break;
     42            case WAY: msg = marktr("Resolve version conflicts for way {0}"); break;
     43            case RELATION: msg = marktr("Resolve version conflicts for relation {0}"); break;
    4444        }
    4545        return new DefaultMutableTreeNode(
    4646                new JLabel(
    47                         tr(msg,conflict.getMy().id),
     47                        tr(msg,conflict.getMy().getId()),
    4848                        ImageProvider.get("data", "object"),
    4949                        JLabel.HORIZONTAL
     
    5555    public boolean executeCommand() {
    5656        super.executeCommand();
    57         conflict.getMy().version = Math.max(conflict.getMy().version, conflict.getTheir().version);
     57        conflict.getMy().setOsmId(
     58                conflict.getMy().getId(),
     59                (int)Math.max(conflict.getMy().getVersion(), conflict.getTheir().getVersion())
     60        );
    5861        getLayer().getConflicts().remove(conflict);
    5962        rememberConflict(conflict);
  • trunk/src/org/openstreetmap/josm/command/WayNodesConflictResolverCommand.java

    r1910 r2070  
    5252        return new DefaultMutableTreeNode(
    5353                new JLabel(
    54                         tr("Resolve conflicts in node list of of way {0}", conflict.getMy().id),
     54                        tr("Resolve conflicts in node list of of way {0}", conflict.getMy().getId()),
    5555                        ImageProvider.get("data", "object"),
    5656                        JLabel.HORIZONTAL
  • trunk/src/org/openstreetmap/josm/data/osm/Changeset.java

    r1990 r2070  
    3030
    3131    public int compareTo(OsmPrimitive other) {
    32         if (other instanceof Changeset) return Long.valueOf(id).compareTo(other.id);
     32        if (other instanceof Changeset) return Long.valueOf(getId()).compareTo(other.getId());
    3333        return 1;
    3434    }
     
    3737    public String getName() {
    3838        // no translation
    39         return "changeset " + id;
     39        return "changeset " + getId();
    4040    }
    4141
    4242    @Override
    4343    public String getLocalName(){
    44         return tr("Changeset {0}",id);
     44        return tr("Changeset {0}",getId());
    4545    }
    4646
  • trunk/src/org/openstreetmap/josm/data/osm/DataSet.java

    r2025 r2070  
    373373    protected void deleteWay(Way way) {
    374374        way.setNodes(null);
    375         way.delete(true);
     375        way.setDeleted(true);
    376376    }
    377377
     
    471471        return false;
    472472    }
     473
     474    public Set<Relation> getReferringRelations(Collection<? extends OsmPrimitive> primitives) {
     475        HashSet<Relation> ret = new HashSet<Relation>();
     476        if (primitives == null) return ret;
     477        Set<? extends OsmPrimitive> referred;
     478        if (primitives instanceof Set<?>) {
     479            referred = (Set<? extends OsmPrimitive>)primitives;
     480        } else {
     481            referred = new HashSet<OsmPrimitive>(primitives);
     482        }
     483        referred.remove(null); // just in case - remove null element from primitives
     484        for (Relation r: relations) {
     485            if (r.isDeleted() || r.incomplete) {
     486                continue;
     487            }
     488            Set<OsmPrimitive> memberPrimitives = r.getMemberPrimitives();
     489            memberPrimitives.retainAll(referred);
     490            if (!memberPrimitives.isEmpty()) {
     491                ret.add(r);
     492            }
     493        }
     494        return ret;
     495    }
    473496}
  • trunk/src/org/openstreetmap/josm/data/osm/Node.java

    r2017 r2070  
    1717
    1818    public final void setCoor(LatLon coor) {
    19         if(coor != null)
    20         {
     19        if(coor != null){
    2120            if(this.coor == null) {
    2221                this.coor = new CachedLatLon(coor);
     
    4746
    4847
     48    /**
     49     * Create a new local node with id 0.
     50     *
     51     */
     52    public Node() {
     53        this(0);
     54    }
     55
    4956
    5057    /**
     
    5259     */
    5360    public Node(long id) {
    54         this.id = id;
    55         incomplete = true;
     61        super(id);
    5662    }
    5763
     
    6066     */
    6167    public Node(Node clone) {
     68        super(clone.getId());
    6269        cloneFrom(clone);
    6370    }
    6471
    6572    public Node(LatLon latlon) {
     73        super(0);
    6674        setCoor(latlon);
    6775    }
    6876
    6977    public Node(EastNorth eastNorth) {
     78        super(0);
    7079        setEastNorth(eastNorth);
    7180    }
     
    8190
    8291    @Override public String toString() {
    83         if (coor == null) return "{Node id="+id+"}";
    84         return "{Node id="+id+",version="+version+",lat="+coor.lat()+",lon="+coor.lon()+"}";
     92        if (coor == null) return "{Node id="+getId()+"}";
     93        return "{Node id="+getId()+",version="+getVersion()+",lat="+coor.lat()+",lon="+coor.lon()+"}";
    8594    }
    8695
     
    101110
    102111    public int compareTo(OsmPrimitive o) {
    103         return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1;
     112        return o instanceof Node ? Long.valueOf(getId()).compareTo(o.getId()) : 1;
    104113    }
    105114
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r2067 r2070  
    1010import java.util.Date;
    1111import java.util.HashMap;
     12import java.util.HashSet;
     13import java.util.LinkedList;
     14import java.util.List;
    1215import java.util.Locale;
    1316import java.util.Map;
     17import java.util.Set;
    1418import java.util.Map.Entry;
    1519
     
    3135abstract public class OsmPrimitive implements Comparable<OsmPrimitive> {
    3236
     37    static public <T extends OsmPrimitive>  List<T> getFilteredList(Collection<OsmPrimitive> list, Class<T> type) {
     38        if (list == null) return null;
     39        List<T> ret = new LinkedList<T>();
     40        for(OsmPrimitive p: list) {
     41            if (type.isInstance(p)) {
     42                ret.add(type.cast(p));
     43            }
     44        }
     45        return ret;
     46    }
     47
     48    static public <T extends OsmPrimitive>  Set<T> getFilteredSet(Collection<OsmPrimitive> set, Class<T> type) {
     49        if (set == null) return null;
     50        HashSet<T> ret = new HashSet<T>();
     51        for(OsmPrimitive p: set) {
     52            if (type.isInstance(p)) {
     53                ret.add(type.cast(p));
     54            }
     55        }
     56        return ret;
     57    }
     58
     59
    3360    /* mappaint data */
    3461    public ElemStyle mappaintStyle = null;
     
    7097     * the respective class.
    7198     *
    72      * @deprecated use {@see #getId()}. Don't assign an id, create a primitive with
     99     * @deprecated use {@see #getId()} and {@see #setId()}. Don't assign an id, create a primitive with
    73100     * the respective constructors.
    74101     */
     
    120147
    121148    /**
     149     * If set to true, this object is incomplete, which means only the id
     150     * and type is known (type is the objects instance class)
     151     */
     152    public boolean incomplete = false;
     153
     154    /**
     155     * Contains the version number as returned by the API. Needed to
     156     * ensure update consistency
     157     * @deprecated use {@see #getVersion()} and {@see #setVersion(long)}
     158     */
     159    @Deprecated
     160    public int version = 0;
     161
     162
     163    /**
     164     * Creates a new primitive with id 0.
     165     *
     166     */
     167    public OsmPrimitive() {
     168        this(0);
     169    }
     170
     171    /**
     172     * Creates a new primitive for the given id. If the id > 0, the primitive is marked
     173     * as incomplete.
     174     *
     175     * @param id the id. > 0 required
     176     * @throws IllegalArgumentException thrown if id < 0
     177     */
     178    public OsmPrimitive(long id) throws IllegalArgumentException {
     179        if (id < 0)
     180            throw new IllegalArgumentException(tr("expected id >= 0. Got {0}", id));
     181        this.id = id;
     182        this.version = 0;
     183        this.incomplete = id >0;
     184    }
     185
     186    /* ------------------------------------------------------------------------------------ */
     187    /* accessors                                                                            */
     188    /* ------------------------------------------------------------------------------------ */
     189
     190    /**
    122191     * Sets whether this primitive is selected or not.
    123192     *
     
    162231     *
    163232     * @return <code>true</code>, if the object has been deleted.
    164      * @see #delete(boolean)
     233     * @see #setDeleted(boolean)
    165234     */
    166235    public boolean isDeleted() {
     
    205274
    206275    /**
     276     * Replies the version number as returned by the API. The version is 0 if the id is 0 or
     277     * if this primitive is incomplete.
     278     *
     279     * @see #setVersion(int)
     280     */
     281    public long getVersion() {
     282        return version;
     283    }
     284
     285    /**
    207286     * Replies the id of this primitive.
    208287     *
     
    214293
    215294    /**
    216      * If set to true, this object is highlighted. Currently this is only used to
    217      * show which ways/nodes will connect
    218      */
    219     public volatile boolean highlighted = false;
    220 
    221     private int timestamp;
     295     * Sets the id and the version of this primitive if it is known to the OSM API.
     296     *
     297     * Since we know the id and its version it can't be incomplete anymore. incomplete
     298     * is set to false.
     299     *
     300     * @param id the id. > 0 required
     301     * @param version the version > 0 required
     302     * @throws IllegalArgumentException thrown if id <= 0
     303     * @throws IllegalArgumentException thrown if version <= 0
     304     */
     305    public void setOsmId(long id, int version) {
     306        if (id <= 0)
     307            throw new IllegalArgumentException(tr("id > 0 expected. Got {0}", id));
     308        if (version <= 0)
     309            throw new IllegalArgumentException(tr("version > 0 expected. Got {0}", version));
     310        this.id = id;
     311        this.version = version;
     312        this.incomplete = false;
     313    }
     314
     315    /**
     316     * Clears the id and version known to the OSM API. The id and the version is set to 0.
     317     * incomplete is set to false.
     318     *
     319     * <strong>Caution</strong>: Do not use this method on primitives which are already added to a {@see DataSet}.
     320     * Ways and relations might already refer to the primitive and clearing the OSM ID
     321     * result in corrupt data.
     322     *
     323     * Here's an example use case:
     324     * <pre>
     325     *     // create a clone of an already existing node
     326     *     Node copy = new Node(otherExistingNode);
     327     *
     328     *     // reset the clones OSM id
     329     *     copy.clearOsmId();
     330     * </pre>
     331     *
     332     */
     333    public void clearOsmId() {
     334        this.id = 0;
     335        this.version = 0;
     336        this.incomplete = false;
     337    }
    222338
    223339    public void setTimestamp(Date timestamp) {
     
    240356
    241357    /**
    242      * If set to true, this object is incomplete, which means only the id
    243      * and type is known (type is the objects instance class)
    244      */
    245     public boolean incomplete = false;
    246 
    247     /**
    248      * Contains the version number as returned by the API. Needed to
    249      * ensure update consistency
    250      */
    251     public int version = -1;
     358     * If set to true, this object is highlighted. Currently this is only used to
     359     * show which ways/nodes will connect
     360     */
     361    public volatile boolean highlighted = false;
     362
     363    private int timestamp;
    252364
    253365    private static Collection<String> uninteresting = null;
     
    288400    abstract public void visit(Visitor visitor);
    289401
    290     public final void delete(boolean deleted) {
     402    /**
     403     * Sets whether this primitive is deleted or not.
     404     *
     405     * Also marks this primitive as modified if deleted is true and sets selected to false.
     406     *
     407     * @param deleted  true, if this primitive is deleted; false, otherwise
     408     */
     409    public void setDeleted(boolean deleted) {
     410        this.modified = deleted;
    291411        this.deleted = deleted;
    292         setSelected(false);
    293         modified = true;
     412        this.selected = false;
    294413    }
    295414
  • trunk/src/org/openstreetmap/josm/data/osm/Relation.java

    r2017 r2070  
    22
    33import java.util.ArrayList;
     4import java.util.HashSet;
    45import java.util.List;
     6import java.util.Set;
    57
    68import org.openstreetmap.josm.data.osm.visitor.Visitor;
     
    1719     * All members of this relation. Note that after changing this,
    1820     * makeBackReferences and/or removeBackReferences should be called.
    19      */
     21     *
     22     * @deprecated use the improved API instead of accessing this list directly
     23     */
     24    @Deprecated
    2025    public final List<RelationMember> members = new ArrayList<RelationMember>();
    2126
     
    104109
    105110    /**
     111     * Create a new relation with id 0
     112     */
     113    public Relation() {
     114
     115    }
     116
     117    /**
    106118     * Create an identical clone of the argument (including the id)
    107119     */
    108120    public Relation(Relation clone) {
     121        super(clone.getId());
    109122        cloneFrom(clone);
    110123    }
    111124
    112125    /**
    113      * Create an incomplete Relation.
    114      */
    115     public Relation(long id) {
    116         this.id = id;
    117         incomplete = true;
    118     }
    119 
    120     /**
    121      * Create an empty Relation. Use this only if you set meaningful values
    122      * afterwards.
    123      */
    124     public Relation() {
    125     }
     126     * Creates a new relation for the given id. If the id > 0, the way is marked
     127     * as incomplete.
     128     *
     129     * @param id the id. > 0 required
     130     * @throws IllegalArgumentException thrown if id < 0
     131     */
     132    public Relation(long id) throws IllegalArgumentException {
     133        super(id);
     134    }
     135
    126136
    127137    @Override public void cloneFrom(OsmPrimitive osm) {
     
    138148        // return "{Relation id="+id+" version="+version+" members="+Arrays.toString(members.toArray())+"}";
    139149        // adding members in string increases memory usage a lot and overflows for looped relations
    140         return "{Relation id="+id+" version="+version+"}";
     150        return "{Relation id="+getId()+" version="+getVersion()+"}";
    141151    }
    142152
     
    152162
    153163    public int compareTo(OsmPrimitive o) {
    154         return o instanceof Relation ? Long.valueOf(id).compareTo(o.id) : -1;
     164        return o instanceof Relation ? Long.valueOf(getId()).compareTo(o.getId()) : -1;
    155165    }
    156166
     
    158168    public boolean isIncomplete() {
    159169        for (RelationMember m : members)
    160             if (m.member == null)
     170            if (m.getMember() == null)
    161171                return true;
    162172        return false;
     
    183193        ArrayList<RelationMember> todelete = new ArrayList<RelationMember>();
    184194        for (RelationMember member: members) {
    185             if (member.member == primitive) {
     195            if (member.getMember() == primitive) {
    186196                todelete.add(member);
    187197            }
     
    194204        return formatter.format(this);
    195205    }
     206
     207    /**
     208     * Replies the set of  {@see OsmPrimitive}s referred to by at least one
     209     * member of this relation
     210     *
     211     * @return the set of  {@see OsmPrimitive}s referred to by at least one
     212     * member of this relation
     213     */
     214    public Set<OsmPrimitive> getMemberPrimitives() {
     215        HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
     216        for (RelationMember m: members) {
     217            if (m.getMember() != null) {
     218                ret.add(m.getMember());
     219            }
     220        }
     221        return ret;
     222    }
    196223}
  • trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java

    r1937 r2070  
    66 * list is not sufficient.
    77 *
    8  * @author Frederik Ramm <frederik@remote.org>
    98 */
    109public class RelationMember {
    1110
     11    /**
     12     *
     13     * @deprecated use {@see #getRole()} or create a clone in order to assign a new role
     14     */
     15    @Deprecated
    1216    public String role;
     17
     18    /**
     19     *
     20     * @deprecated use {@see #getMember()} or create a clone in order to assign a new member
     21     */
     22    @Deprecated
    1323    public OsmPrimitive member;
    1424
     
    1929     */
    2030    public String getRole() {
    21         if (role == null) {
     31        if (role == null)
    2232            return "";
    23         } else {
    24             return role;
    25         }
     33        return role;
    2634    }
    2735
     
    4553
    4654    /**
    47     *
    48     * @return True if member is way
    49     * @since 1937
    50     */
     55     *
     56     * @return True if member is way
     57     * @since 1937
     58     */
    5159    public boolean isWay() {
    5260        return member instanceof Way;
     
    5462
    5563    /**
    56     *
    57     * @return True if member is node
    58     * @since 1937
    59     */
     64     *
     65     * @return True if member is node
     66     * @since 1937
     67     */
    6068    public boolean isNode() {
    6169        return member instanceof Node;
     
    7280
    7381    /**
    74     *
    75     * @return Member as way
    76     * @since 1937
    77     */
     82     *
     83     * @return Member as way
     84     * @since 1937
     85     */
    7886    public Way getWay() {
    7987        return (Way)member;
     
    8189
    8290    /**
    83     *
    84     * @return Member as node
    85     * @since 1937
    86     */
     91     *
     92     * @return Member as node
     93     * @since 1937
     94     */
    8795    public Node getNode() {
    8896        return (Node)member;
     
    9098
    9199    /**
    92     *
    93     * @return Member
    94     * @since 1937
    95     */
     100     *
     101     * @return Member
     102     * @since 1937
     103     */
    96104    public OsmPrimitive getMember() {
    97105        return member;
     
    101109    /**
    102110     * Default constructor. Does nothing.
    103      * @deprecated Use other constructors because RelationMember class will became immutable
     111     * @deprecated Use other constructors because RelationMember class will become immutable
    104112     * in the future
    105113     */
  • trunk/src/org/openstreetmap/josm/data/osm/Tag.java

    r2008 r2070  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.data.osm;
     3
     4import java.util.ArrayList;
     5import java.util.Collections;
     6import java.util.Iterator;
     7import java.util.List;
    38
    49/**
     
    2732     */
    2833    public Tag(String key) {
     34        this();
    2935        this.key = key == null ? "" : key;
    3036    }
  • trunk/src/org/openstreetmap/josm/data/osm/TagCollection.java

    r2017 r2070  
    66import java.util.ArrayList;
    77import java.util.Collection;
     8import java.util.Collections;
    89import java.util.HashMap;
    910import java.util.HashSet;
     
    571572            throw new IllegalStateException(tr("tag collection can't be applied to a primitive because there are keys with multiple values"));
    572573        for (Tag tag: tags) {
    573             primitive.put(tag.getKey(), tag.getValue());
     574            if (tag.getValue() == null || tag.getValue().equals("")) {
     575                primitive.remove(tag.getKey());
     576            } else {
     577                primitive.put(tag.getKey(), tag.getValue());
     578            }
    574579        }
    575580    }
     
    682687        return ret;
    683688    }
     689
     690    /**
     691     * Replies the concatenation of all tag values (concatenated by a semicolon)
     692     *
     693     * @return the concatenation of all tag values
     694     */
     695    public String getJoinedValues(String key) {
     696        StringBuffer buffer = new StringBuffer();
     697        List<String> values = new ArrayList<String>(getValues(key));
     698        values.remove("");
     699        Collections.sort(values);
     700        Iterator<String> iter = values.iterator();
     701        while (iter.hasNext()) {
     702            buffer.append(iter.next());
     703            if (iter.hasNext()) {
     704                buffer.append(";");
     705            }
     706        }
     707        return buffer.toString();
     708    }
    684709}
  • trunk/src/org/openstreetmap/josm/data/osm/User.java

    r2034 r2070  
    22package org.openstreetmap.josm.data.osm;
    33
     4import java.util.ArrayList;
    45import java.util.HashMap;
     6import java.util.List;
    57
    68/**
     
    1113 * is only one user object.
    1214 *
    13  * @author fred
    1415 *
    1516 */
    1617public class User {
     18    static private long uidCounter = 0;
     19    /**
     20     * the map of known users
     21     */
     22    private static HashMap<Long,User> userMap = new HashMap<Long,User>();
    1723
    18     /** storage for existing User objects. */
    19     private static HashMap<String,User> userMap = new HashMap<String,User>();
    2024
    21     /** the username. */
    22     public String name;
     25    private static long getNextLocalUid() {
     26        synchronized(User.class) {
     27            uidCounter--;
     28            return uidCounter;
     29        }
     30    }
    2331
    24     /** the user ID (since API 0.6) */
    25     public String uid;
     32    /**
     33     * Creates a local user with the given name
     34     *
     35     * @param name the name
     36     */
     37    public static User createLocalUser(String name) {
     38        User user = new User(getNextLocalUid(), name);
     39        userMap.put(user.getId(), user);
     40        return user;
     41    }
     42
     43    /**
     44     * Creates a user known to the OSM server
     45     *
     46     * @param uid  the user id
     47     * @param name the name
     48     */
     49    public static User  createOsmUser(long uid, String name) {
     50        User user = new User(uid, name);
     51        userMap.put(user.getId(), user);
     52        return user;
     53    }
     54
     55
     56    /**
     57     * Returns the user with user id <code>uid</code> or null if this user doesn't exist
     58     *
     59     * @param uid the user id
     60     * @return the user; null, if there is no user with  this id
     61     */
     62    public static User getById(long uid) {
     63        return userMap.get(uid);
     64    }
     65
     66    /**
     67     * Returns the list of users with name <code>name</code> or the empty list if
     68     * no such users exist
     69     *
     70     * @param name the user name
     71     * @return the list of users with name <code>name</code> or the empty list if
     72     * no such users exist
     73     */
     74    public static List<User> getByName(String name) {
     75        name = name == null ? "" : name;
     76        List<User> ret = new ArrayList<User>();
     77        for (User user: userMap.values()) {
     78            if (user.getName().equals(name)) {
     79                ret.add(user);
     80            }
     81        }
     82        return ret;
     83    }
     84
     85    /** the user name */
     86    private String name;
     87    /** the user id */
     88    private long uid;
     89
     90    /**
     91     * Replies the user name
     92     *
     93     * @return the user name. Never null, but may be the empty string
     94     */
     95    public String getName() {
     96        return name == null ? "" : name;
     97    }
     98
     99    /**
     100     * Replies the user id. If this user is known to the OSM server the positive user id
     101     * from the server is replied. Otherwise, a negative local value is replied.
     102     *
     103     * A negative local is only unique during an editing session. It is lost when the
     104     * application is closed and there is no guarantee that a negative local user id is
     105     * always bound to a user with the same name.
     106     *
     107     */
     108    public long getId() {
     109        return uid;
     110    }
    26111
    27112    /** private constructor, only called from get method. */
    28     private User(String name) {
     113    private User(long uid, String name) {
     114        this.uid = uid;
    29115        this.name = name;
    30116    }
    31117
    32     /** returns a new or existing User object that represents the given name. */
    33     public static User get(String name) {
    34         User user = userMap.get(name);
    35         if (user == null) {
    36             user = new User(name);
    37             userMap.put(name, user);
    38         }
    39         return user;
     118    public boolean isOsmUser() {
     119        return uid > 0;
     120    }
     121
     122    public boolean isLocalUser() {
     123        return uid < 0;
    40124    }
    41125
     
    45129        int result = 1;
    46130        result = prime * result + ((name == null) ? 0 : name.hashCode());
    47         result = prime * result + ((uid == null) ? 0 : uid.hashCode());
     131        result = prime * result + (int) (uid ^ (uid >>> 32));
    48132        return result;
    49133    }
     
    63147        } else if (!name.equals(other.name))
    64148            return false;
    65         if (uid == null) {
    66             if (other.uid != null)
    67                 return false;
    68         } else if (!uid.equals(other.uid))
     149        if (uid != other.uid)
    69150            return false;
    70151        return true;
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r2017 r2070  
    119119
    120120    /**
     121     * Creates a new way with id 0.
     122     *
     123     */
     124    public Way(){
     125    }
     126
     127    /**
    121128     * Create an identical clone of the argument (including the id).
    122129     *
     
    124131     */
    125132    public Way(Way original) {
     133        super(original.getId());
    126134        cloneFrom(original);
    127135    }
    128136
    129137    /**
    130      * Create an empty way without id. Use this only if you set meaningful
    131      * values yourself.
    132      */
    133     public Way() {
    134     }
    135 
    136     /**
    137      * Create an incomplete Way with a given id.
    138      *
    139      * @param id  the id. id > 0 required.
    140      */
    141     public Way(long id) {
    142         // FIXME: shouldn't we check for id > 0?
    143         //
    144         this.id = id;
    145         incomplete = true;
     138     * Creates a new way for the given id. If the id > 0, the way is marked
     139     * as incomplete.
     140     *
     141     * @param id the id. > 0 required
     142     * @throws IllegalArgumentException thrown if id < 0
     143     */
     144    public Way(long id) throws IllegalArgumentException {
     145        super(id);
    146146    }
    147147
     
    153153
    154154    @Override public String toString() {
    155         if (incomplete) return "{Way id="+id+" version="+version+" (incomplete)}";
    156         return "{Way id="+id+" version="+version+" nodes="+Arrays.toString(nodes.toArray())+"}";
     155        if (incomplete) return "{Way id="+getId()+" version="+getVersion()+" (incomplete)}";
     156        return "{Way id="+getId()+" version="+getVersion()+" nodes="+Arrays.toString(nodes.toArray())+"}";
    157157    }
    158158
     
    170170        if (o instanceof Relation)
    171171            return 1;
    172         return o instanceof Way ? Long.valueOf(id).compareTo(o.id) : -1;
     172        return o instanceof Way ? Long.valueOf(getId()).compareTo(o.getId()) : -1;
    173173    }
    174174
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/CollectBackReferencesVisitor.java

    r1937 r2070  
    4646    public void visit(Node n) {
    4747        for (Way w : ds.ways) {
    48             if (w.deleted || w.incomplete) continue;
     48            if (w.isDeleted() || w.incomplete) {
     49                continue;
     50            }
    4951            for (Node n2 : w.getNodes()) {
    5052                if (n == n2) {
     
    7274        // references.
    7375        for (Relation r : ds.relations) {
    74             if (r.incomplete || r.deleted) continue;
     76            if (r.incomplete || r.isDeleted()) {
     77                continue;
     78            }
    7579            for (RelationMember m : r.getMembers()) {
    7680                if (m.getMember() == p) {
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java

    r2025 r2070  
    236236        if (myPrimitivesWithDefinedIds.containsKey(other.getId())) {
    237237            P my = myPrimitivesWithDefinedIds.get(other.getId());
    238             if (my.version <= other.version) {
     238            if (my.getVersion() <= other.getVersion()) {
    239239                if (! my.isVisible() && other.isVisible()) {
    240240                    // should not happen
     
    243243                            + "their primitive with lower version {2} is not visible. "
    244244                            + "Can't deal with this inconsistency. Keeping my primitive. ",
    245                             Long.toString(my.getId()),Long.toString(my.version), Long.toString(other.version)
     245                            Long.toString(my.getId()),Long.toString(my.getVersion()), Long.toString(other.getVersion())
    246246                    ));
    247247                    merged.put(other, my);
     
    270270                    //
    271271                    merged.put(other, my);
    272                 } else if (my.isDeleted() && ! other.isDeleted() && my.version == other.version) {
     272                } else if (my.isDeleted() && ! other.isDeleted() && my.getVersion() == other.getVersion()) {
    273273                    // same version, but my is deleted. Assume mine takes precedence
    274274                    // otherwise too many conflicts when refreshing from the server
     
    288288                    my.cloneFrom(other);
    289289                    merged.put(other, my);
    290                 } else if (! my.isModified() && !other.isModified() && my.version == other.version) {
     290                } else if (! my.isModified() && !other.isModified() && my.getVersion() == other.getVersion()) {
    291291                    // both not modified. Keep mine
    292292                    //
    293293                    merged.put(other,my);
    294                 } else if (! my.isModified() && !other.isModified() && my.version < other.version) {
     294                } else if (! my.isModified() && !other.isModified() && my.getVersion() < other.getVersion()) {
    295295                    // my not modified but other is newer. clone other onto mine.
    296296                    //
    297297                    my.cloneFrom(other);
    298298                    merged.put(other,my);
    299                 } else if (my.isModified() && ! other.isModified() && my.version == other.version) {
     299                } else if (my.isModified() && ! other.isModified() && my.getVersion() == other.getVersion()) {
    300300                    // my is same as other but mine is modified
    301301                    // => keep mine
  • trunk/src/org/openstreetmap/josm/gui/ConditionalOptionPaneUtil.java

    r2032 r2070  
    2727 *
    2828 */
    29 @Deprecated public class ConditionalOptionPaneUtil {
     29public class ConditionalOptionPaneUtil {
    3030    static public final int DIALOG_DISABLED_OPTION = Integer.MIN_VALUE;
    3131
  • trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java

    r1997 r2070  
    7474    protected String decorateNameWithId(String name, OsmPrimitive primitive) {
    7575        if (Main.pref.getBoolean("osm-primitives.showid"))
    76             return name + tr(" [id: {0}]", primitive.id);
     76            return name + tr(" [id: {0}]", primitive.getId());
    7777        else
    7878            return name;
     
    9696            }
    9797            if (name == null) {
    98                 name = node.id == 0 ? tr("node") : ""+ node.id;
     98                name = node.getId() == 0 ? tr("node") : ""+ node.getId();
    9999            }
    100100            name += " (" + node.getCoor().latToString(CoordinateFormat.getDefaultFormat()) + ", " + node.getCoor().lonToString(CoordinateFormat.getDefaultFormat()) + ")";
     
    175175            }
    176176            if (nameTag == null) {
    177                 name += Long.toString(relation.id) + ", ";
     177                name += Long.toString(relation.getId()) + ", ";
    178178            } else {
    179179                name += "\"" + nameTag + "\", ";
     
    197197     */
    198198    public String format(Changeset changeset) {
    199         return tr("Changeset {0}",changeset.id);
     199        return tr("Changeset {0}",changeset.getId());
    200200    }
    201201}
  • trunk/src/org/openstreetmap/josm/gui/OsmPrimitivRenderer.java

    r1990 r2070  
    33
    44import java.awt.Component;
     5import java.util.ArrayList;
     6import java.util.Collections;
    57
    68import javax.swing.DefaultListCellRenderer;
     
    6264            ((JLabel)def).setText(value.getDisplayName(DefaultNameFormatter.getInstance()));
    6365            ((JLabel)def).setIcon(ImageProvider.get(OsmPrimitiveType.from(value)));
     66            ((JLabel)def).setToolTipText(buildToolTipText(value));
    6467        }
    6568        return def;
    6669    }
    6770
     71    /**
     72     * build the tool tip text for an {@see OsmPrimitive}. It consist of the formatted
     73     * key/value pairs for this primitive.
     74     *
     75     * @param primitive
     76     * @return the tool tip text
     77     */
     78    public String buildToolTipText(OsmPrimitive primitive) {
     79        StringBuilder sb = new StringBuilder();
     80
     81        sb.append("<html>");
     82        // show the id
     83        //
     84        sb.append("<strong>id</strong>=")
     85        .append(primitive.getId())
     86        .append("<br>");
     87
     88        // show the key/value-pairs, sorted by key
     89        //
     90        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
     91        Collections.sort(keyList);
     92        for (int i = 0; i < keyList.size(); i++) {
     93            if (i > 0) {
     94                sb.append("<br>");
     95            }
     96            String key = keyList.get(i);
     97            sb.append("<strong>")
     98            .append(key)
     99            .append("</strong>")
     100            .append("=");
     101            // make sure long values are split into several rows. Otherwise
     102            // the tool tip window can become to wide
     103            //
     104            String value = primitive.get(key);
     105            while(value.length() != 0) {
     106                sb.append(value.substring(0,Math.min(50, value.length())));
     107                if (value.length() > 50) {
     108                    sb.append("<br>");
     109                    value = value.substring(50);
     110                } else {
     111                    value = "";
     112                }
     113            }
     114        }
     115        sb.append("</html>");
     116        return sb.toString();
     117    }
    68118}
  • trunk/src/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModel.java

    r1954 r2070  
    7676    @Override
    7777    public boolean isEqualEntry(Node e1, Node e2) {
    78         if (e1.id > 0)
    79             return e1.id == e2.id;
     78        if (e1.getId() > 0)
     79            return e1.getId() == e2.getId();
    8080        else
    8181            return e1 == e2;
  • trunk/src/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMergeModel.java

    r2017 r2070  
    166166        }
    167167
    168         myDeletedState = my.deleted;
    169         theirDeletedState = their.deleted;
    170 
    171         myVisibleState = my.visible;
    172         theirVisibleState = their.visible;
     168        myDeletedState = my.isDeleted();
     169        theirDeletedState = their.isDeleted();
     170
     171        myVisibleState = my.isVisible();
     172        theirVisibleState = their.isVisible();
    173173
    174174        coordMergeDecision = UNDECIDED;
     
    474474                        + "<br>"
    475475                        + "Do you want to undelete these nodes too?</html>",
    476                         Long.toString(dependent.size()), Long.toString(way.id)),
     476                        Long.toString(dependent.size()), Long.toString(way.getId())),
    477477                        tr("Undelete additional nodes?"),
    478478                        JOptionPane.YES_NO_OPTION,
     
    503503                        + "<br>"
    504504                        + "Do you want to undelete them too?</html>",
    505                         Long.toString(dependent.size()), Long.toString(r.id)),
     505                        Long.toString(dependent.size()), Long.toString(r.getId())),
    506506                        tr("Undelete dependent primitives?"),
    507507                        JOptionPane.YES_NO_OPTION,
     
    535535        HashMap<Long,OsmPrimitive> candidates = new HashMap<Long,OsmPrimitive>();
    536536        for (Node n : way.getNodes()) {
    537             if (n.id > 0 && ! candidates.values().contains(n)) {
    538                 candidates.put(n.id, n);
     537            if (n.getId() > 0 && ! candidates.values().contains(n)) {
     538                candidates.put(n.getId(), n);
    539539            }
    540540        }
     
    545545        ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>();
    546546        for (OsmPrimitive their : ds.allPrimitives()) {
    547             if (candidates.keySet().contains(their.id) && ! their.visible) {
    548                 toDelete.add(candidates.get(their.id));
     547            if (candidates.keySet().contains(their.getId()) && ! their.isVisible()) {
     548                toDelete.add(candidates.get(their.getId()));
    549549            }
    550550        }
     
    572572        HashMap<Long,OsmPrimitive> candidates = new HashMap<Long, OsmPrimitive>();
    573573        for (RelationMember m : r.getMembers()) {
    574             if (m.getMember().id > 0 && !candidates.values().contains(m.getMember())) {
    575                 candidates.put(m.getMember().id, m.getMember());
     574            if (m.getMember().getId() > 0 && !candidates.values().contains(m.getMember())) {
     575                candidates.put(m.getMember().getId(), m.getMember());
    576576            }
    577577        }
     
    583583        ArrayList<OsmPrimitive> toDelete = new ArrayList<OsmPrimitive>();
    584584        for (OsmPrimitive their : ds.allPrimitives()) {
    585             if (candidates.keySet().contains(their.id) && ! their.visible) {
    586                 toDelete.add(candidates.get(their.id));
     585            if (candidates.keySet().contains(their.getId()) && ! their.isVisible()) {
     586                toDelete.add(candidates.get(their.getId()));
    587587            }
    588588        }
  • trunk/src/org/openstreetmap/josm/gui/conflict/pair/properties/PropertiesMerger.java

    r2017 r2070  
    586586                            + "it a new id.<br>"
    587587                            + "Do yo agree?</html>",
    588                             model.getMyPrimitive().id
     588                            model.getMyPrimitive().getId()
    589589                    ),
    590590                    tr("Reset id to 0"),
     
    625625                            + "from the dataset.<br>"
    626626                            + "Do you agree?</html>",
    627                             model.getMyPrimitive().id
     627                            model.getMyPrimitive().getId()
    628628                    ),
    629629                    tr("Remove from dataset"),
  • trunk/src/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberListMergeModel.java

    r1954 r2070  
    2525    public boolean isEqualEntry(RelationMember e1, RelationMember e2) {
    2626        boolean ret = e1.getRole().equals(e2.getRole());
    27         if (e1.getMember().id > 0 ) {
    28             ret = ret && (e1.getMember().id == e2.getMember().id);
     27        if (e1.getMember().getId() > 0 ) {
     28            ret = ret && (e1.getMember().getId() == e2.getMember().getId());
    2929        } else {
    3030            ret = ret && (e1 == e2);
     
    4242            public boolean isCellEditable(int row, int column) {
    4343                switch(column) {
    44                 case 1: return true;
    45                 default: return false;
     44                    case 1: return true;
     45                    default: return false;
    4646                }
    4747            }
  • trunk/src/org/openstreetmap/josm/gui/conflict/pair/relation/RelationMemberTableCellRenderer.java

    r1990 r2070  
    7171        sb.append("<html>");
    7272        sb.append("<strong>id</strong>=")
    73         .append(primitive.id)
     73        .append(primitive.getId())
    7474        .append("<br>");
    7575        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
     
    197197        renderForeground(getModel(table), member, row, column, isSelected);
    198198        switch(column) {
    199         case 0:
    200             renderRowId(row);
    201             break;
    202         case 1:
    203             if (member == null) {
    204                 renderEmptyRow();
    205             } else {
    206                 renderRole(member);
    207             }
    208             break;
    209         case 2:
    210             if (member == null) {
    211                 renderEmptyRow();
    212             } else {
    213                 renderPrimitive(member);
    214             }
    215             break;
    216         default:
    217             // should not happen
     199            case 0:
     200                renderRowId(row);
     201                break;
     202            case 1:
     203                if (member == null) {
     204                    renderEmptyRow();
     205                } else {
     206                    renderRole(member);
     207                }
     208                break;
     209            case 2:
     210                if (member == null) {
     211                    renderEmptyRow();
     212                } else {
     213                    renderPrimitive(member);
     214                }
     215                break;
     216            default:
     217                // should not happen
    218218        }
    219219        return this;
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/MultiValueResolutionDecision.java

    r2008 r2070  
    127127
    128128    /**
    129      * Replies the concatenation of all tag values (concatenated by a semicolon)
    130      *
    131      * @return the concatenation of all tag values
    132      */
    133     protected String joinValues() {
    134         StringBuffer buffer = new StringBuffer();
    135         List<String> values = new ArrayList<String>(tags.getValues());
    136         values.remove("");
    137         Collections.sort(values);
    138         Iterator<String> iter = values.iterator();
    139         while (iter.hasNext()) {
    140             buffer.append(iter.next());
    141             if (iter.hasNext()) {
    142                 buffer.append(";");
    143             }
    144         }
    145         return buffer.toString();
    146     }
    147 
    148     /**
    149129     * Replies the chosen value
    150130     *
     
    157137            case KEEP_ONE: return value;
    158138            case KEEP_NONE: return null;
    159             case KEEP_ALL: return joinValues();
     139            case KEEP_ALL: return tags.getJoinedValues(getKey());
    160140        }
    161141        // should not happen
     
    171151        ArrayList<String> ret = new ArrayList<String>(tags.getValues());
    172152        ret.remove("");
     153        ret.remove(null);
    173154        Collections.sort(ret);
    174155        return ret;
     
    303284    public Tag getResolution() {
    304285        switch(type) {
    305             case KEEP_ALL: return new Tag(getKey(), joinValues());
     286            case KEEP_ALL: return new Tag(getKey(), tags.getJoinedValues(getKey()));
    306287            case KEEP_ONE: return new Tag(getKey(),value);
    307288            case KEEP_NONE: return new Tag(getKey(), "");
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/PasteTagsConflictResolverDialog.java

    r2008 r2070  
    148148        String msg = "";
    149149        switch(type) {
    150             case NODE: msg= trn("{0} node", "{0} nodes", count, count); break;
    151             case WAY: msg= trn("{0} way", "{0} ways", count, count); break;
    152             case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break;
     150        case NODE: msg= trn("{0} node", "{0} nodes", count, count); break;
     151        case WAY: msg= trn("{0} way", "{0} ways", count, count); break;
     152        case RELATION: msg= trn("{0} relation", "{0} relations", count, count); break;
    153153        }
    154154        return msg;
     
    295295
    296296        public void propertyChange(PropertyChangeEvent evt) {
    297             if (evt.getPropertyName().equals(TagConflictResolverModel.RESOLVED_COMPLETELY_PROP)) {
     297            if (evt.getPropertyName().equals(TagConflictResolverModel.NUM_CONFLICTS_PROP)) {
    298298                updateEnabledState();
    299299            }
     
    324324
    325325    public void propertyChange(PropertyChangeEvent evt) {
    326         if (evt.getPropertyName().equals(TagConflictResolverModel.RESOLVED_COMPLETELY_PROP)) {
     326        if (evt.getPropertyName().equals(TagConflictResolverModel.NUM_CONFLICTS_PROP)) {
    327327            TagConflictResolverModel model = (TagConflictResolverModel)evt.getSource();
    328328            for (int i=0; i < tpResolvers.getTabCount();i++) {
     
    446446                String msg = "";
    447447                switch(type) {
    448                     case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
    449                     case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
    450                     case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
     448                case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
     449                case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
     450                case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
    451451                }
    452452                text = text.equals("") ? msg : text + ", " + msg;
     
    472472                String msg = "";
    473473                switch(type) {
    474                     case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
    475                     case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
    476                     case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
     474                case NODE: msg = trn("{0} node", "{0} nodes", numPrimitives,numPrimitives); break;
     475                case WAY: msg = trn("{0} way", "{0} ways", numPrimitives, numPrimitives); break;
     476                case RELATION: msg = trn("{0} relation", "{0} relations", numPrimitives, numPrimitives); break;
    477477                }
    478478                text = text.equals("") ? msg : text + ", " + msg;
     
    491491
    492492                switch(column) {
    493                     case 0: renderNumTags(info); break;
    494                     case 1: renderFrom(info); break;
    495                     case 2: renderTo(info); break;
     493                case 0: renderNumTags(info); break;
     494                case 1: renderFrom(info); break;
     495                case 2: renderTo(info); break;
    496496                }
    497497            }
  • trunk/src/org/openstreetmap/josm/gui/conflict/tags/TagConflictResolverModel.java

    r2017 r2070  
    1717
    1818public class TagConflictResolverModel extends DefaultTableModel {
    19     static public final String RESOLVED_COMPLETELY_PROP = TagConflictResolverModel.class.getName() + ".resolvedCompletely";
     19    static public final String NUM_CONFLICTS_PROP = TagConflictResolverModel.class.getName() + ".numConflicts";
    2020
    2121    private TagCollection tags;
    2222    private List<String> keys;
    2323    private HashMap<String, MultiValueResolutionDecision> decisions;
    24     private boolean resolvedCompletely;
     24    private int numConflicts;
    2525    private PropertyChangeSupport support;
    2626
    2727    public TagConflictResolverModel() {
    28         resolvedCompletely= false;
     28        numConflicts = 0;
    2929        support = new PropertyChangeSupport(this);
    3030    }
     
    3838    }
    3939
    40     protected void setResolvedCompletely(boolean resolvedCompletey) {
    41         boolean oldValue = this.resolvedCompletely;
    42         this.resolvedCompletely = resolvedCompletey;
    43         if (oldValue != this.resolvedCompletely) {
    44             support.firePropertyChange(RESOLVED_COMPLETELY_PROP, oldValue, this.resolvedCompletely);
     40
     41    protected void setNumConflicts(int numConflicts) {
     42        int oldValue = this.numConflicts;
     43        this.numConflicts = numConflicts;
     44        if (oldValue != this.numConflicts) {
     45            support.firePropertyChange(NUM_CONFLICTS_PROP, oldValue, this.numConflicts);
    4546        }
    4647    }
    4748
    48     protected void refreshResolvedCompletely() {
     49    protected void refreshNumConflicts() {
     50        int count = 0;
    4951        for (MultiValueResolutionDecision d : decisions.values()) {
    5052            if (!d.isDecided()) {
    51                 setResolvedCompletely(false);
    52                 return;
     53                count++;
    5354            }
    5455        }
    55         setResolvedCompletely(true);
     56        setNumConflicts(count);
    5657    }
    5758
     
    7879            decisions.put(key,decision);
    7980        }
    80         refreshResolvedCompletely();
     81        refreshNumConflicts();
    8182    }
    8283
     
    126127        }
    127128        fireTableDataChanged();
    128         refreshResolvedCompletely();
     129        refreshNumConflicts();
    129130    }
    130131
     
    136137     */
    137138    public boolean isResolvedCompletely() {
    138         return resolvedCompletely;
     139        return numConflicts == 0;
     140    }
     141
     142    public int getNumConflicts() {
     143        return numConflicts;
     144    }
     145
     146    public int getNumDecisions() {
     147        return getRowCount();
    139148    }
    140149
     
    146155        return tc;
    147156    }
     157
     158    public MultiValueResolutionDecision getDecision(int row) {
     159        return decisions.get(keys.get(row));
     160    }
     161
     162    public void refresh() {
     163        fireTableDataChanged();
     164        refreshNumConflicts();
     165    }
    148166}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/HistoryDialog.java

    r2019 r2070  
    220220                return;
    221221            for (OsmPrimitive primitive: Main.main.getCurrentDataSet().getSelected()) {
    222                 if (primitive.id == 0) {
     222                if (primitive.getId() == 0) {
    223223                    continue;
    224224                }
    225                 History h = HistoryDataSet.getInstance().getHistory(primitive.id);
     225                History h = HistoryDataSet.getInstance().getHistory(primitive.getId());
    226226                if (h !=null) {
    227227                    data.add(h);
  • trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java

    r2025 r2070  
    379379
    380380        public void launchEditorForDuplicate(Relation original) {
    381             Relation copy = new Relation();
     381            Relation copy = new Relation(original.getId());
    382382            copy.cloneFrom(original);
    383             // FIXME: id is going to be hidden. How to reset a primitive?
    384             //
    385             copy.id = 0;
     383            copy.clearOsmId();
    386384            copy.setModified(true);
    387385            RelationEditor editor = RelationEditor.getEditor(
  • trunk/src/org/openstreetmap/josm/gui/dialogs/UserListDialog.java

    r2042 r2070  
    201201            User user = (User)infoObject;
    202202            try {
    203                 return getBaseUserUrl() + "/" + URLEncoder.encode(user.name, "UTF-8");
     203                return getBaseUserUrl() + "/" + URLEncoder.encode(user.getName(), "UTF-8");
    204204            } catch(UnsupportedEncodingException e) {
    205205                e.printStackTrace();
     
    251251            if (count < o.count) return 1;
    252252            if (count > o.count) return -1;
    253             if (user== null || user.name == null) return 1;
    254             if (o.user == null || o.user.name == null) return -1;
    255             return user.name.compareTo(o.user.name);
     253            if (user== null || user.getName() == null) return 1;
     254            if (o.user == null || o.user.getName() == null) return -1;
     255            return user.getName().compareTo(o.user.getName());
    256256        }
    257257
    258258        public String getName() {
    259259            if (user == null) return null;
    260             return user.name;
     260            return user.getName();
    261261        }
    262262    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java

    r2017 r2070  
    361361         */
    362362        protected void rememberChildRelationsToDownload(Relation parent) {
    363             downloadedRelationIds.add(parent.id);
     363            downloadedRelationIds.add(parent.getId());
    364364            for (RelationMember member: parent.getMembers()) {
    365365                if (member.isRelation()) {
     
    403403                while(! relationsToDownload.isEmpty() && !cancelled) {
    404404                    Relation r = relationsToDownload.pop();
    405                     if (r.id == 0) {
     405                    if (r.getId() == 0) {
    406406                        continue;
    407407                    }
    408408                    rememberChildRelationsToDownload(r);
    409409                    progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
    410                     OsmServerObjectReader reader = new OsmServerObjectReader(r.id, OsmPrimitiveType.RELATION,
     410                    OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
    411411                            true);
    412412                    DataSet dataSet = null;
     
    514514                while(it.hasNext() && !cancelled) {
    515515                    Relation r = it.next();
    516                     if (r.id == 0) {
     516                    if (r.getId() == 0) {
    517517                        continue;
    518518                    }
    519519                    progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
    520                     OsmServerObjectReader reader = new OsmServerObjectReader(r.id, OsmPrimitiveType.RELATION,
     520                    OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
    521521                            true);
    522522                    DataSet dataSet = reader.parseOsm(progressMonitor
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableCellRenderer.java

    r1822 r2070  
    3535        StringBuilder sb = new StringBuilder();
    3636        sb.append("<html>");
    37         sb.append("<strong>id</strong>=").append(primitive.id).append("<br>");
     37        sb.append("<strong>id</strong>=").append(primitive.getId()).append("<br>");
    3838        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
    3939        Collections.sort(keyList);
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java

    r1951 r2070  
    198198    public void updateMemberReferences(DataSet ds) {
    199199        for (RelationMember member : members) {
    200             if (member.getMember().id == 0) {
     200            if (member.getMember().getId() == 0) {
    201201                continue;
    202202            }
    203             OsmPrimitive primitive = ds.getPrimitiveById(member.getMember().id);
     203            OsmPrimitive primitive = ds.getPrimitiveById(member.getMember().getId());
    204204            if (primitive != null) {
    205205                member.member = primitive;
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ParentRelationLoadingTask.java

    r2017 r2070  
    7070     * @exception IllegalArgumentException thrown if child is null
    7171     * @exception IllegalArgumentException thrown if layer is null
    72      * @exception IllegalArgumentException thrown if child.id == 0
     72     * @exception IllegalArgumentException thrown if child.getId() == 0
    7373     */
    7474    public ParentRelationLoadingTask(Relation child, OsmDataLayer layer, boolean full, PleaseWaitProgressMonitor monitor ) {
     
    7878        if (layer == null)
    7979            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
    80         if (child.id == 0)
    81             throw new IllegalArgumentException(tr("child.id >0 expected. Got {1}", child.id));
     80        if (child.getId() == 0)
     81            throw new IllegalArgumentException(tr("child.getId() >0 expected. Got {1}", child.getId()));
    8282        referrers = null;
    8383        this.layer = layer;
     
    150150        parents.clear();
    151151        for (Relation parent : referrers.relations) {
    152             parents.add((Relation)getLayer().data.getPrimitiveById(parent.id));
     152            parents.add((Relation)getLayer().data.getPrimitiveById(parent.getId()));
    153153        }
    154154        if (continuation != null) {
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowserModel.java

    r1938 r2070  
    8989
    9090    public boolean canReload() {
    91         return relation != null && relation.id > 0;
     91        return relation != null && relation.getId() > 0;
    9292    }
    9393
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationDialogManager.java

    r1857 r2070  
    9090        @Override
    9191        public String toString() {
    92             return "[Context: layer=" + layer.getName() + ",relation=" + relation.id + "]";
     92            return "[Context: layer=" + layer.getName() + ",relation=" + relation.getId() + "]";
    9393        }
    9494    }
     
    103103        openDialogs = new HashMap<DialogContext, RelationEditor>();
    104104    }
    105 
    106105    /**
    107106     * Register the relation editor for a relation managed by a
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationEditor.java

    r1871 r2070  
    117117        if (getRelation() == null) {
    118118            setTitle(tr("Create new relation in layer ''{0}''", layer.getName()));
    119         } else if (getRelation().id == 0) {
     119        } else if (getRelation().getId() == 0) {
    120120            setTitle(tr("Edit new relation in layer ''{0}''", layer.getName()));
    121121        } else {
    122             setTitle(tr("Edit relation #{0} in layer ''{1}''", relation.id, layer.getName()));
     122            setTitle(tr("Edit relation #{0} in layer ''{1}''", relation.getId(), layer.getName()));
    123123        }
    124124    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java

    r1828 r2070  
    9191            TreePath path  = event.getPath();
    9292            Relation parent = (Relation)event.getPath().getLastPathComponent();
    93             if (! parent.incomplete || parent.id == 0)
     93            if (! parent.incomplete || parent.getId() == 0)
    9494                // we don't load complete  or new relations
    9595                return;
     
    154154        protected void realRun() throws SAXException, IOException, OsmTransferException {
    155155            try {
    156                 OsmServerObjectReader reader = new OsmServerObjectReader(relation.id, OsmPrimitiveType.from(relation), true /* full load */);
     156                OsmServerObjectReader reader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.from(relation), true /* full load */);
    157157                ds = reader.parseOsm(progressMonitor
    158158                        .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/SelectionTableCellRenderer.java

    r1990 r2070  
    6060        sb.append("<html>");
    6161        sb.append("<strong>id</strong>=")
    62         .append(primitive.id)
     62        .append(primitive.getId())
    6363        .append("<br>");
    6464        ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
  • trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java

    r2019 r2070  
    6868        if (primitive == null)
    6969            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "primitive"));
    70         return add(primitive.id, OsmPrimitiveType.from(primitive));
     70        return add(primitive.getId(), OsmPrimitiveType.from(primitive));
    7171    }
    7272
  • trunk/src/org/openstreetmap/josm/gui/tagging/AutoCompletingTextField.java

    r2048 r2070  
    77import java.awt.event.KeyAdapter;
    88import java.awt.event.KeyEvent;
     9import java.util.ArrayList;
     10import java.util.EventObject;
     11import java.util.LinkedList;
     12import java.util.List;
    913import java.util.logging.Logger;
    1014
    1115import javax.swing.ComboBoxEditor;
     16import javax.swing.JTable;
    1217import javax.swing.JTextField;
     18import javax.swing.event.CellEditorListener;
     19import javax.swing.event.ChangeEvent;
     20import javax.swing.table.TableCellEditor;
    1321import javax.swing.text.AttributeSet;
    1422import javax.swing.text.BadLocationException;
     
    1725
    1826import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
     27import org.openstreetmap.josm.gui.util.TableCellEditorSupport;
    1928
    2029/**
     
    2736 *
    2837 */
    29 public class AutoCompletingTextField extends JTextField implements ComboBoxEditor {
     38public class AutoCompletingTextField extends JTextField implements ComboBoxEditor, TableCellEditor {
    3039
    3140    static private Logger logger = Logger.getLogger(AutoCompletingTextField.class.getName());
     
    133142                }
    134143        );
     144        tableCellEditorSupport = new TableCellEditorSupport(this);
    135145    }
    136146
     
    187197    }
    188198
    189 
     199    /* ------------------------------------------------------------------------------------ */
     200    /* TableCellEditor interface                                                            */
     201    /* ------------------------------------------------------------------------------------ */
     202
     203    private TableCellEditorSupport tableCellEditorSupport;
     204    private String originalValue;
     205
     206    public void addCellEditorListener(CellEditorListener l) {
     207        tableCellEditorSupport.addCellEditorListener(l);
     208    }
     209
     210    protected void rememberOriginalValue(String value) {
     211        this.originalValue = value;
     212    }
     213
     214    protected void restoreOriginalValue() {
     215        setText(originalValue);
     216    }
     217
     218    public void removeCellEditorListener(CellEditorListener l) {
     219        tableCellEditorSupport.removeCellEditorListener(l);
     220    }
     221    public void cancelCellEditing() {
     222        restoreOriginalValue();
     223        tableCellEditorSupport.fireEditingCanceled();
     224
     225    }
     226
     227    public Object getCellEditorValue() {
     228        return getText();
     229    }
     230
     231    public boolean isCellEditable(EventObject anEvent) {
     232        return true;
     233    }
     234
     235
     236    public boolean shouldSelectCell(EventObject anEvent) {
     237        return true;
     238    }
     239
     240    public boolean stopCellEditing() {
     241        tableCellEditorSupport.fireEditingStopped();
     242        return true;
     243    }
     244
     245    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
     246        setText( value == null ? "" : value.toString());
     247        rememberOriginalValue(getText());
     248        return this;
     249    }
    190250}
  • trunk/src/org/openstreetmap/josm/io/DiffResultReader.java

    r2039 r2070  
    9494    public void visit(Node n) {
    9595        String key = "node:" + (newIdMap.containsKey(n) ? newIdMap.get(n) : n.getId());
    96         System.out.println("key: "+key);
    9796        Long[] nv = versions.get(key);
    9897        if (nv != null) {
    9998            processed.add(n);
    10099            if (!n.isDeleted()) {
    101                 n.id = nv[0]; n.version = nv[1].intValue();
     100                n.setOsmId(nv[0], nv[1].intValue());
    102101            }
    103102        }
     
    109108            processed.add(w);
    110109            if (!w.isDeleted()) {
    111                 w.id = nv[0]; w.version = nv[1].intValue();
     110                w.setOsmId(nv[0], nv[1].intValue());
    112111            }
    113112        }
     
    119118            processed.add(r);
    120119            if (!r.isDeleted()) {
    121                 r.id = nv[0]; r.version = nv[1].intValue();
     120                r.setOsmId(nv[0], nv[1].intValue());
    122121            }
    123122        }
  • trunk/src/org/openstreetmap/josm/io/FileImporter.java

    r1637 r2070  
    2121    }
    2222
    23     public void importData(File file) throws IOException {
     23    public void importData(File file) throws IOException, IllegalDataException {
    2424        throw new IOException(tr("Could not read \"{0}\"", file.getName()));
    2525    }
  • trunk/src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java

    r2025 r2070  
    204204        remember(relation.getId(), OsmPrimitiveType.RELATION);
    205205        for (RelationMember member : relation.getMembers()) {
    206             if (OsmPrimitiveType.from(member.member).equals(OsmPrimitiveType.RELATION)) {
     206            if (OsmPrimitiveType.from(member.getMember()).equals(OsmPrimitiveType.RELATION)) {
    207207                // avoid infinite recursion in case of cyclic dependencies in relations
    208208                //
    209                 if (relations.contains(member.member.getId())) {
     209                if (relations.contains(member.getMember().getId())) {
    210210                    continue;
    211211                }
     
    325325        progressMonitor.subTask(tr("Downloading OSM data..."));
    326326        try {
    327             final OsmReader osm = OsmReader.parseDataSetOsm(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
    328             merge(osm.getDs());
     327
     328            merge(
     329                    OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))
     330            );
    329331        } catch(Exception e) {
    330332            throw new OsmTransferException(e);
     
    348350        progressMonitor.subTask(tr("Downloading OSM data..."));
    349351        try {
    350             final OsmReader osm = OsmReader.parseDataSetOsm(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
    351             merge(osm.getDs());
     352            merge(
     353                    OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false))
     354            );
    352355        } catch(Exception e) {
    353356            throw new OsmTransferException(e);
  • trunk/src/org/openstreetmap/josm/io/OsmApi.java

    r2061 r2070  
    233233        try {
    234234            ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/create", toXml(osm, true),monitor);
    235             osm.id = Long.parseLong(ret.trim());
    236             osm.version = 1;
     235            osm.setOsmId(Long.parseLong(ret.trim()), 1);
    237236        } catch(NumberFormatException e){
    238237            throw new OsmTransferException(tr("unexpected format of id replied by the server, got ''{0}''", ret));
     
    258257            try {
    259258                ret = sendRequest("PUT", OsmPrimitiveType.from(osm).getAPIName()+"/" + osm.getId(), toXml(osm, true), monitor);
    260                 osm.version = Integer.parseInt(ret.trim());
     259                osm.setOsmId(osm.getId(), Integer.parseInt(ret.trim()));
    261260            } catch(NumberFormatException e) {
    262261                throw new OsmTransferException(tr("unexpected format of new version of modified primitive ''{0}'', got ''{1}''", osm.getId(), ret));
     
    341340                    return;
    342341                }
    343                 changeset.id = this.changeset.getId();
    344                 this.changeset.cloneFrom(changeset);
     342                this.changeset.setKeys(changeset.getKeys());
    345343                progressMonitor.setCustomText(tr("Updating changeset {0}...", changeset.getId()));
    346344                sendRequest(
     
    473471     */
    474472    private String sendRequest(String requestMethod, String urlSuffix,String requestBody, ProgressMonitor monitor) throws OsmTransferException {
    475 
    476473        StringBuffer responseBody = new StringBuffer();
    477474
  • trunk/src/org/openstreetmap/josm/io/OsmBzip2Importer.java

    r2001 r2070  
    1111import org.apache.tools.bzip2.CBZip2InputStream;
    1212import org.openstreetmap.josm.actions.ExtensionFileFilter;
    13 import org.xml.sax.SAXException;
    1413
    1514public class OsmBzip2Importer extends OsmImporter {
     
    2120
    2221    @Override
    23     public void importData(File file) throws IOException {
     22    public void importData(File file) throws IOException, IllegalDataException {
    2423        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
    2524        int b = bis.read();
    26         if (b != 'B') {
     25        if (b != 'B')
    2726            throw new IOException(tr("Invalid bz2 file."));
    28         }
    2927        b = bis.read();
    30         if (b != 'Z') {
     28        if (b != 'Z')
    3129            throw new IOException(tr("Invalid bz2 file."));
    32         }
    3330        CBZip2InputStream in = new CBZip2InputStream(bis);
    34 
    35         try {
    36             importData(in, file);
    37         } catch (SAXException e) {
    38             e.printStackTrace();
    39             throw new IOException(tr("Could not read \"{0}\"", file.getName()));
    40         }
     31        importData(in, file);
    4132    }
    4233}
  • trunk/src/org/openstreetmap/josm/io/OsmGzipImporter.java

    r1653 r2070  
    1010
    1111import org.openstreetmap.josm.actions.ExtensionFileFilter;
    12 import org.xml.sax.SAXException;
    1312
    1413public class OsmGzipImporter extends OsmImporter {
     
    1918
    2019    @Override
    21     public void importData(File file) throws IOException {
     20    public void importData(File file) throws IOException, IllegalDataException {
    2221        GZIPInputStream in = new GZIPInputStream(new FileInputStream(file));
    23         try {
    24             importData(in, file);
    25         } catch (SAXException e) {
    26             e.printStackTrace();
    27             throw new IOException(tr("Could not read \"{0}\"", file.getName()));
    28         }
     22        importData(in, file);
    2923    }
    3024}
  • trunk/src/org/openstreetmap/josm/io/OsmImporter.java

    r2050 r2070  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.awt.HeadlessException;
    76import java.io.File;
    87import java.io.FileInputStream;
     
    1817import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    1918import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    20 import org.xml.sax.SAXException;
    2119
    2220public class OsmImporter extends FileImporter {
     
    3028    }
    3129
    32     @Override public void importData(File file) throws IOException {
     30    @Override public void importData(File file) throws IOException, IllegalDataException {
    3331        try {
    3432            FileInputStream in = new FileInputStream(file);
    3533            importData(in, file);
    36         } catch (HeadlessException e) {
    37             e.printStackTrace();
    38             throw new IOException(tr("Could not read \"{0}\"", file.getName()));
    3934        } catch (FileNotFoundException e) {
    4035            e.printStackTrace();
    4136            throw new IOException(tr("File \"{0}\" does not exist", file.getName()));
    42         } catch (SAXException e) {
    43             e.printStackTrace();
    44             throw new IOException(tr("Parsing file \"{0}\" failed", file.getName()));
    4537        }
    4638    }
    4739
    48     protected void importData(InputStream in, File associatedFile) throws SAXException, IOException {
    49         OsmReader osm = OsmReader.parseDataSetOsm(in, NullProgressMonitor.INSTANCE);
    50         DataSet dataSet = osm.getDs();
     40    protected void importData(InputStream in, File associatedFile) throws IllegalDataException {
     41        DataSet dataSet = OsmReader.parseDataSet(in, NullProgressMonitor.INSTANCE);
    5142        final OsmDataLayer layer = new OsmDataLayer(dataSet, associatedFile.getName(), associatedFile);
    5243        // FIXME: remove UI stuff from IO subsystem
  • trunk/src/org/openstreetmap/josm/io/OsmReader.java

    r1951 r2070  
    1 // License: GPL. Copyright 2007 by Immanuel Scholz and others
    21package org.openstreetmap.josm.io;
    32
    43import static org.openstreetmap.josm.tools.I18n.tr;
    54
    6 import java.io.IOException;
    75import java.io.InputStream;
    86import java.io.InputStreamReader;
     
    1412import java.util.List;
    1513import java.util.Map;
    16 import java.util.Map.Entry;
    1714import java.util.logging.Logger;
    1815
     
    3431import org.xml.sax.Attributes;
    3532import org.xml.sax.InputSource;
     33import org.xml.sax.Locator;
    3634import org.xml.sax.SAXException;
    3735import org.xml.sax.helpers.DefaultHandler;
     
    4038 * Parser for the Osm Api. Read from an input stream and construct a dataset out of it.
    4139 *
    42  * Reading process takes place in three phases. During the first phase (including xml parse),
    43  * all nodes are read and stored. Other information than nodes are stored in a raw list
    44  *
    45  * The second phase read all ways out of the remaining objects in the raw list.
    46  *
    47  * @author Imi
    4840 */
    4941public class OsmReader {
     
    5446     */
    5547    private DataSet ds = new DataSet();
    56     public DataSet getDs() { return ds; }
    57 
    58     /**
    59      * All read nodes after phase 1.
    60      */
    61     private Map<Long, Node> nodes = new HashMap<Long, Node>();
     48
     49    /**
     50     * Replies the parsed data set
     51     *
     52     * @return the parsed data set
     53     */
     54    public DataSet getDataSet() {
     55        return ds;
     56    }
     57
     58    /** the map from external ids to read OsmPrimitives. External ids are
     59     * longs too, but in contrast to internal ids negative values are used
     60     * to identify primitives unknown to the OSM server
     61     *
     62     * The keys are strings composed as follows
     63     * <ul>
     64     *   <li>"n" + id  for nodes</li>
     65     *   <li>"w" + id  for nodes</li>
     66     *   <li>"r" + id  for nodes</li>
     67     * </ul>
     68     */
     69    private Map<String, OsmPrimitive> externalIdMap = new HashMap<String, OsmPrimitive>();
    6270
    6371
     
    6977     */
    7078    private OsmReader() {
     79        externalIdMap = new HashMap<String, OsmPrimitive>();
    7180    }
    7281
    7382    private static class OsmPrimitiveData {
    7483        public long id = 0;
    75         public Map<String,String> keys = new HashMap<String, String>();
    7684        public boolean modified = false;
    77         public boolean selected = false;
    7885        public boolean deleted = false;
    7986        public Date timestamp = new Date();
    8087        public User user = null;
    8188        public boolean visible = true;
    82         public int version = -1;
     89        public int version = 0;
    8390        public LatLon latlon = new LatLon(0,0);
     91        private OsmPrimitive primitive;
    8492
    8593        public void copyTo(OsmPrimitive osm) {
    86             osm.id = id;
    87             osm.setKeys(keys);
    88             osm.modified = modified;
    89             osm.setSelected(selected);
    90             osm.deleted = deleted;
     94            osm.setModified(modified);
     95            osm.setDeleted(deleted);
     96            //  id < 0 possible if read from a file
     97            if (id <= 0) {
     98                osm.clearOsmId();
     99            } else {
     100                osm.setOsmId(id, version);
     101            }
    91102            osm.setTimestamp(timestamp);
    92103            osm.user = user;
    93             osm.visible = visible;
    94             osm.version = version;
     104            osm.setVisible(visible);
    95105            osm.mappaintStyle = null;
    96106        }
    97107
    98108        public Node createNode() {
    99             Node node = new Node(latlon);
     109            Node node = new Node();
     110            node.setCoor(latlon);
    100111            copyTo(node);
     112            primitive = node;
    101113            return node;
    102114        }
    103115
    104116        public Way createWay() {
    105             Way way = new Way(id);
     117            Way way = new Way();
    106118            copyTo(way);
     119            primitive = way;
    107120            return way;
    108121        }
    109 
    110122        public Relation createRelation() {
    111             Relation rel = new Relation(id);
    112             copyTo(rel);
    113             return rel;
     123            Relation relation= new Relation();
     124            copyTo(relation);
     125            primitive = relation;
     126            return relation;
     127        }
     128
     129        public void rememberTag(String key, String value) {
     130            primitive.put(key, value);
    114131        }
    115132    }
     
    128145     * Data structure for the remaining way objects
    129146     */
    130     private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>();
     147    private Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>();
    131148
    132149    /**
    133150     * Data structure for relation objects
    134151     */
    135     private Map<OsmPrimitiveData, Collection<RelationMemberData>> relations = new HashMap<OsmPrimitiveData, Collection<RelationMemberData>>();
     152    private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>();
     153
     154    static public class OsmDataParsingException extends SAXException {
     155        private int columnNumber;
     156        private int lineNumber;
     157
     158        public OsmDataParsingException() {
     159            super();
     160        }
     161
     162        public OsmDataParsingException(Exception e) {
     163            super(e);
     164        }
     165
     166        public OsmDataParsingException(String message, Exception e) {
     167            super(message, e);
     168        }
     169
     170        public OsmDataParsingException(String message) {
     171            super(message);
     172        }
     173
     174        public OsmDataParsingException rememberLocation(Locator locator) {
     175            if (locator == null) return this;
     176            this.columnNumber = locator.getColumnNumber();
     177            this.lineNumber = locator.getLineNumber();
     178            return this;
     179        }
     180
     181        @Override
     182        public String getMessage() {
     183            String msg = super.getMessage();
     184            if (lineNumber == 0 && columnNumber == 0)
     185                return msg;
     186            if (msg == null) {
     187                msg = getClass().getName();
     188            }
     189            msg = msg + " " + tr("(at line {0}, column {1})", lineNumber, columnNumber);
     190            return msg;
     191        }
     192
     193        public int getColumnNumber() {
     194            return columnNumber;
     195        }
     196
     197        public int getLineNumber() {
     198            return lineNumber;
     199        }
     200    }
    136201
    137202    private class Parser extends DefaultHandler {
     203        private Locator locator;
     204
     205        @Override
     206        public void setDocumentLocator(Locator locator) {
     207            this.locator = locator;
     208        }
     209
     210        protected void throwException(String msg) throws OsmDataParsingException{
     211            throw new OsmDataParsingException(msg).rememberLocation(locator);
     212        }
    138213        /**
    139214         * The current osm primitive to be read.
     
    143218
    144219        @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
    145             try {
    146                 if (qName.equals("osm")) {
    147                     if (atts == null)
    148                         throw new SAXException(tr("Unknown version"));
    149                     String v = atts.getValue("version");
    150                     if (v == null)
    151                         throw new SAXException(tr("Version number missing from OSM data"));
    152                     if (!(v.equals("0.5") || v.equals("0.6")))
    153                         throw new SAXException(tr("Unknown version: {0}", v));
    154                     // save generator attribute for later use when creating DataSource objects
    155                     generator = atts.getValue("generator");
    156                     ds.version = v;
    157 
    158                 } else if (qName.equals("bounds")) {
    159                     // new style bounds.
    160                     String minlon = atts.getValue("minlon");
    161                     String minlat = atts.getValue("minlat");
    162                     String maxlon = atts.getValue("maxlon");
    163                     String maxlat = atts.getValue("maxlat");
    164                     String origin = atts.getValue("origin");
    165                     if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
    166                         if (origin == null) {
    167                             origin = generator;
    168                         }
    169                         Bounds bounds = new Bounds(
    170                                 new LatLon(Double.parseDouble(minlat), Double.parseDouble(minlon)),
    171                                 new LatLon(Double.parseDouble(maxlat), Double.parseDouble(maxlon)));
    172                         DataSource src = new DataSource(bounds, origin);
    173                         ds.dataSources.add(src);
     220            if (qName.equals("osm")) {
     221                if (atts == null) {
     222                    throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}", "version", "osm"));
     223                }
     224                String v = atts.getValue("version");
     225                if (v == null) {
     226                    throwException(tr("Missing mandatory attribute ''{0}''", "version"));
     227                }
     228                if (!(v.equals("0.5") || v.equals("0.6"))) {
     229                    throwException(tr("Unsupported version: {0}", v));
     230                }
     231                // save generator attribute for later use when creating DataSource objects
     232                generator = atts.getValue("generator");
     233                ds.version = v;
     234
     235            } else if (qName.equals("bounds")) {
     236                // new style bounds.
     237                String minlon = atts.getValue("minlon");
     238                String minlat = atts.getValue("minlat");
     239                String maxlon = atts.getValue("maxlon");
     240                String maxlat = atts.getValue("maxlat");
     241                String origin = atts.getValue("origin");
     242                if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
     243                    if (origin == null) {
     244                        origin = generator;
    174245                    }
    175 
    176                     // ---- PARSING NODES AND WAYS ----
    177 
    178                 } else if (qName.equals("node")) {
    179                     current = new OsmPrimitiveData();
    180                     current.latlon = new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon"));
    181                     readCommon(atts, current);
    182                 } else if (qName.equals("way")) {
    183                     current = new OsmPrimitiveData();
    184                     readCommon(atts, current);
    185                     ways.put(current, new ArrayList<Long>());
    186                 } else if (qName.equals("nd")) {
    187                     Collection<Long> list = ways.get(current);
    188                     if (list == null)
    189                         throw new SAXException(tr("Found <nd> element in non-way."));
    190                     long id = getLong(atts, "ref");
    191                     if (id == 0)
    192                         throw new SAXException(tr("<nd> has zero ref"));
    193                     list.add(id);
    194 
    195                     // ---- PARSING RELATIONS ----
    196 
    197                 } else if (qName.equals("relation")) {
    198                     current = new OsmPrimitiveData();
    199                     readCommon(atts, current);
    200                     relations.put(current, new LinkedList<RelationMemberData>());
    201                 } else if (qName.equals("member")) {
    202                     Collection<RelationMemberData> list = relations.get(current);
    203                     if (list == null)
    204                         throw new SAXException(tr("Found <member> element in non-relation."));
    205                     RelationMemberData emd = new RelationMemberData();
    206                     String value = atts.getValue("ref");
    207                     if (value == null)
    208                         throw new SAXException(tr("Missing attribute \"ref\" on member in relation {0}",current.id));
    209                     try {
    210                         emd.id = Long.parseLong(value);
    211                     } catch(NumberFormatException e) {
    212                         throw new SAXException(tr("Illegal value for attribute \"ref\" on member in relation {0}, got {1}", Long.toString(current.id),value));
    213                     }
    214                     value = atts.getValue("type");
    215                     if (value == null)
    216                         throw new SAXException(tr("Missing attribute \"type\" on member {0} in relation {1}", Long.toString(emd.id), Long.toString(current.id)));
    217                     if (! (value.equals("way") || value.equals("node") || value.equals("relation")))
    218                         throw new SAXException(tr("Unexpected \"type\" on member {0} in relation {1}, got {2}.", Long.toString(emd.id), Long.toString(current.id), value));
    219                     emd.type= value;
    220                     value = atts.getValue("role");
    221                     emd.role = value;
    222 
    223                     if (emd.id == 0)
    224                         throw new SAXException(tr("Incomplete <member> specification with ref=0"));
    225 
    226                     list.add(emd);
    227 
    228                     // ---- PARSING TAGS (applicable to all objects) ----
    229 
    230                 } else if (qName.equals("tag")) {
    231                     String key = atts.getValue("k");
    232                     String value = atts.getValue("v");
    233                     current.keys.put(key,value);
    234                 }
    235             } catch (NumberFormatException x) {
    236                 x.printStackTrace(); // SAXException does not chain correctly
    237                 throw new SAXException(x.getMessage(), x);
    238             } catch (NullPointerException x) {
    239                 x.printStackTrace(); // SAXException does not chain correctly
    240                 throw new SAXException(tr("NullPointerException, possibly some missing tags."), x);
    241             }
    242         }
    243 
    244         @Override
    245         public void endElement(String uri, String localName, String qName) throws SAXException {
    246             if (qName.equals("node")) {
    247                 nodes.put(current.id, current.createNode());
     246                    Bounds bounds = new Bounds(
     247                            new LatLon(Double.parseDouble(minlat), Double.parseDouble(minlon)),
     248                            new LatLon(Double.parseDouble(maxlat), Double.parseDouble(maxlon)));
     249                    DataSource src = new DataSource(bounds, origin);
     250                    ds.dataSources.add(src);
     251                } else {
     252                    throwException(tr(
     253                            "Missing manadatory attributes on element ''bounds''. Got minlon=''{0}'',minlat=''{1}00,maxlon=''{3}'',maxlat=''{4}'', origin=''{5}''",
     254                            minlon, minlat, maxlon, maxlat, origin
     255                    ));
     256                }
     257
     258                // ---- PARSING NODES AND WAYS ----
     259
     260            } else if (qName.equals("node")) {
     261                current = new OsmPrimitiveData();
     262                current.latlon = new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon"));
     263                readCommon(atts, current);
     264                Node n = current.createNode();
     265                externalIdMap.put("n"+current.id, n);
     266            } else if (qName.equals("way")) {
     267                current = new OsmPrimitiveData();
     268                readCommon(atts, current);
     269                Way w = current.createWay();
     270                externalIdMap.put("w"+current.id, w);
     271                ways.put(current.id, new ArrayList<Long>());
     272            } else if (qName.equals("nd")) {
     273                Collection<Long> list = ways.get(current.id);
     274                if (list == null) {
     275                    throwException(
     276                            tr("found XML element <nd> element not as direct child of element <way>")
     277                    );
     278                }
     279                if (atts.getValue("ref") == null) {
     280                    throwException(
     281                            tr("Missing mandatory attribute ''{0}'' on <nd> of way {1}", "ref", current.id)
     282                    );
     283                }
     284                long id = getLong(atts, "ref");
     285                if (id == 0) {
     286                    throwException(
     287                            tr("Illegal value of attribute ''ref'' of element <nd>. Got {0}", id)
     288                    );
     289                }
     290                list.add(id);
     291
     292                // ---- PARSING RELATIONS ----
     293
     294            } else if (qName.equals("relation")) {
     295                current = new OsmPrimitiveData();
     296                readCommon(atts, current);
     297                Relation r = current.createRelation();
     298                externalIdMap.put("r"+current.id, r );
     299                relations.put(current.id, new LinkedList<RelationMemberData>());
     300            } else if (qName.equals("member")) {
     301                Collection<RelationMemberData> list = relations.get(current.id);
     302                if (list == null) {
     303                    throwException(
     304                            tr("Found XML element <member> not as direct child of element <relation>")
     305                    );
     306                }
     307                RelationMemberData emd = new RelationMemberData();
     308                String value = atts.getValue("ref");
     309                if (value == null) {
     310                    throwException(tr("Missing attribute ''ref'' on member in relation {0}",current.id));
     311                }
     312                try {
     313                    emd.id = Long.parseLong(value);
     314                } catch(NumberFormatException e) {
     315                    throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(current.id),value));
     316                }
     317                value = atts.getValue("type");
     318                if (value == null) {
     319                    throwException(tr("Missing attribute ''type'' on member {0} in relation {1}", Long.toString(emd.id), Long.toString(current.id)));
     320                }
     321                if (! (value.equals("way") || value.equals("node") || value.equals("relation"))) {
     322                    throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", Long.toString(emd.id), Long.toString(current.id), value));
     323                }
     324                emd.type= value;
     325                value = atts.getValue("role");
     326                emd.role = value;
     327
     328                if (emd.id == 0) {
     329                    throwException(tr("Incomplete <member> specification with ref=0"));
     330                }
     331
     332                list.add(emd);
     333
     334                // ---- PARSING TAGS (applicable to all objects) ----
     335
     336            } else if (qName.equals("tag")) {
     337                String key = atts.getValue("k");
     338                String value = atts.getValue("v");
     339                current.rememberTag(key, value);
    248340            }
    249341        }
     
    252344            return Double.parseDouble(atts.getValue(value));
    253345        }
    254     }
    255 
    256     /**
    257      * Read out the common attributes from atts and put them into this.current.
    258      */
    259     void readCommon(Attributes atts, OsmPrimitiveData current) throws SAXException {
    260         current.id = getLong(atts, "id");
    261         if (current.id == 0)
    262             throw new SAXException(tr("Illegal object with id=0"));
    263 
    264         String time = atts.getValue("timestamp");
    265         if (time != null && time.length() != 0) {
    266             current.timestamp =  DateUtils.fromString(time);
    267         }
    268 
    269         // user attribute added in 0.4 API
    270         String user = atts.getValue("user");
    271         if (user != null) {
    272             // do not store literally; get object reference for string
    273             current.user = User.get(user);
    274         }
    275 
    276         // uid attribute added in 0.6 API
    277         String uid = atts.getValue("uid");
    278         if (uid != null) {
    279             if (current.user != null) {
    280                 current.user.uid = uid;
    281             }
    282         }
    283 
    284         // visible attribute added in 0.4 API
    285         String visible = atts.getValue("visible");
    286         if (visible != null) {
    287             current.visible = Boolean.parseBoolean(visible);
    288         }
    289 
    290         String version = atts.getValue("version");
    291         current.version = 0;
    292         if (version != null) {
     346
     347        private User createUser(String uid, String name) throws SAXException {
     348            if (uid == null) {
     349                if (name == null)
     350                    return null;
     351                return User.createLocalUser(name);
     352            }
    293353            try {
    294                 current.version = Integer.parseInt(version);
     354                long id = Long.parseLong(uid);
     355                return User.createOsmUser(id, name);
    295356            } catch(NumberFormatException e) {
    296                 throw new SAXException(tr("Illegal value for attribute \"version\" on OSM primitive with id {0}, got {1}", Long.toString(current.id), version));
    297             }
    298         } else {
    299             // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
    300             //
    301             if (current.id > 0 && ds.version != null && ds.version.equals("0.6"))
    302                 throw new SAXException(tr("Missing attribute \"version\" on OSM primitive with id {0}", Long.toString(current.id)));
    303         }
    304 
    305         String action = atts.getValue("action");
    306         if (action == null)
    307             return;
    308         if (action.equals("delete")) {
    309             current.deleted = true;
    310         } else if (action.startsWith("modify")) {
    311             current.modified = true;
    312         }
    313     }
    314     private long getLong(Attributes atts, String value) throws SAXException {
    315         String s = atts.getValue(value);
    316         if (s == null)
    317             throw new SAXException(tr("Missing required attribute \"{0}\".",value));
    318         return Long.parseLong(s);
    319     }
    320 
    321     private Node findNode(long id) {
    322         Node n = nodes.get(id);
    323         if (n != null)
    324             return n;
    325         return null;
    326     }
    327 
    328     protected void createWays() {
    329         for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) {
    330             Way w = new Way(e.getKey().id);
     357                throwException(tr("Illegal value for attribute ''uid''. Got ''{0}''", uid));
     358            }
     359            return null;
     360        }
     361        /**
     362         * Read out the common attributes from atts and put them into this.current.
     363         */
     364        void readCommon(Attributes atts, OsmPrimitiveData current) throws SAXException {
     365            current.id = getLong(atts, "id");
     366            if (current.id == 0) {
     367                throwException(tr("Illegal object with id=0"));
     368            }
     369
     370            String time = atts.getValue("timestamp");
     371            if (time != null && time.length() != 0) {
     372                current.timestamp =  DateUtils.fromString(time);
     373            }
     374
     375            // user attribute added in 0.4 API
     376            String user = atts.getValue("user");
     377            // uid attribute added in 0.6 API
     378            String uid = atts.getValue("uid");
     379            current.user = createUser(uid, user);
     380
     381            // visible attribute added in 0.4 API
     382            String visible = atts.getValue("visible");
     383            if (visible != null) {
     384                current.visible = Boolean.parseBoolean(visible);
     385            }
     386
     387            String version = atts.getValue("version");
     388            current.version = 0;
     389            if (version != null) {
     390                try {
     391                    current.version = Integer.parseInt(version);
     392                } catch(NumberFormatException e) {
     393                    throwException(tr("Illegal value for attribute ''version'' on OSM primitive with id {0}. Got {1}", Long.toString(current.id), version));
     394                }
     395                if (current.version <= 0) {
     396                    throwException(tr("Illegal value for attribute ''version'' on OSM primitive with id {0}. Got {1}", Long.toString(current.id), version));
     397                }
     398            } else {
     399                // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
     400                //
     401                if (current.id > 0 && ds.version != null && ds.version.equals("0.6")) {
     402                    throwException(tr("Missing attribute ''version'' on OSM primitive with id {0}", Long.toString(current.id)));
     403                }
     404            }
     405
     406            String action = atts.getValue("action");
     407            if (action == null)
     408                return;
     409            if (action.equals("delete")) {
     410                current.deleted = true;
     411            } else if (action.startsWith("modify")) {
     412                current.modified = true;
     413            }
     414        }
     415
     416        private long getLong(Attributes atts, String name) throws SAXException {
     417            String value = atts.getValue(name);
     418            if (value == null) {
     419                throwException(tr("Missing required attribute ''{0}''.",name));
     420            }
     421            try {
     422                return Long.parseLong(value);
     423            } catch(NumberFormatException e) {
     424                throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''",name, value));
     425            }
     426            return 0; // should not happen
     427        }
     428    }
     429
     430
     431    /**
     432     * Processes the ways after parsing. Rebuilds the list of nodes of each way and
     433     * adds the way to the dataset
     434     *
     435     * @throws IllegalDataException thrown if a data integrity problem is detected
     436     */
     437    protected void processWaysAfterParsing() throws IllegalDataException{
     438        for (Long externalWayId: ways.keySet()) {
     439            Way w = (Way)externalIdMap.get("w" + externalWayId);
    331440            boolean incomplete = false;
    332441            List<Node> wayNodes = new ArrayList<Node>();
    333             for (long id : e.getValue()) {
    334                 Node n = findNode(id);
     442            for (long id : ways.get(externalWayId)) {
     443                Node n = (Node)externalIdMap.get("n" +id);
    335444                if (n == null) {
     445                    if (id <= 0)
     446                        throw new IllegalDataException (
     447                                tr(
     448                                        "way with external id ''{0}'' includes missing node with external id ''{1}''",
     449                                        externalWayId,
     450                                        id
     451                                )
     452                        );
    336453                    n = new Node(id);
    337454                    n.incomplete = true;
     
    343460            if (incomplete) {
    344461                logger.warning(tr("marked way {0} with {1} nodes incomplete because at least one node was missing in the " +
    345                                 "loaded data and is therefore incomplete too", e.getKey().id, w.getNodesCount()));
    346                 e.getKey().copyTo(w);
     462                        "loaded data and is therefore incomplete too", externalWayId, w.getNodesCount()));
    347463                w.incomplete = true;
    348464                ds.addPrimitive(w);
    349465            } else {
    350                 e.getKey().copyTo(w);
    351466                w.incomplete = false;
    352467                ds.addPrimitive(w);
     
    356471
    357472    /**
    358      * Return the Way object with the given id, or null if it doesn't
    359      * exist yet. This method only looks at ways stored in the already parsed
    360      * ways.
    361      *
    362      * @param id
    363      * @return way object or null
    364      */
    365     private Way findWay(long id) {
    366         for (Way way : ds.ways)
    367             if (way.id == id)
    368                 return way;
    369         return null;
    370     }
    371 
    372     /**
    373      * Return the Relation object with the given id, or null if it doesn't
    374      * exist yet. This method only looks at relations in the already parsed
    375      * relations.
    376      *
    377      * @param id
    378      * @return relation object or null
    379      */
    380     private Relation findRelation(long id) {
    381         for (Relation e : ds.relations)
    382             if (e.id == id)
    383                 return e;
    384         return null;
    385     }
    386 
    387     /**
    388      * Create relations. This is slightly different than n/s/w because
    389      * unlike other objects, relations may reference other relations; it
    390      * is not guaranteed that a referenced relation will have been created
    391      * before it is referenced. So we have to create all relations first,
    392      * and populate them later.
    393      */
    394     private void createRelations() throws SAXException {
    395 
    396         // pass 1 - create all relations
    397         for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) {
    398             Relation en = new Relation();
    399             e.getKey().copyTo(en);
    400             ds.addPrimitive(en);
    401         }
    402 
    403         // Cache the ways here for much better search performance
    404         HashMap<Long, Way> hm = new HashMap<Long, Way>(10000);
    405         for (Way wy : ds.ways) {
    406             hm.put(wy.id, wy);
    407         }
    408 
    409         // pass 2 - sort out members
    410         for (Entry<OsmPrimitiveData, Collection<RelationMemberData>> e : relations.entrySet()) {
    411             Relation en = findRelation(e.getKey().id);
    412             if (en == null) throw new Error("Failed to create relation " + e.getKey().id);
    413 
     473     * Processes the parsed nodes after parsing. Just adds them to
     474     * the dataset
     475     *
     476     */
     477    protected void processNodesAfterParsing() {
     478        for (OsmPrimitive primitive: externalIdMap.values()) {
     479            if (primitive instanceof Node) {
     480                this.ds.addPrimitive(primitive);
     481            }
     482        }
     483    }
     484
     485    /**
     486     * Completes the parsed relations with its members.
     487     *
     488     * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a
     489     * relation member refers to a local primitive which wasn't available in the data
     490     *
     491     */
     492    private void processRelationsAfterParsing() throws IllegalDataException {
     493        for (Long externalRelationId : relations.keySet()) {
     494            Relation relation = (Relation) externalIdMap.get("r" +externalRelationId);
    414495            List<RelationMember> relationMembers = new ArrayList<RelationMember>();
    415 
    416             for (RelationMemberData emd : e.getValue()) {
    417                 OsmPrimitive member;
    418                 if (emd.type.equals("node")) {
    419                     member = findNode(emd.id);
    420                     if (member == null) {
    421                         member = new Node(emd.id);
    422                         ds.addPrimitive(member);
     496            for (RelationMemberData rm : relations.get(externalRelationId)) {
     497                OsmPrimitive primitive = null;
     498
     499                // lookup the member from the map of already created primitives
     500                //
     501                if (rm.type.equals("node")) {
     502                    primitive = externalIdMap.get("n" + rm.id);
     503                } else if (rm.type.equals("way")) {
     504                    primitive = externalIdMap.get("w" + rm.id);
     505                } else if (rm.type.equals("relation")) {
     506                    primitive = externalIdMap.get("r" + rm.id);
     507                } else
     508                    throw new IllegalDataException(
     509                            tr("Unknown relation member type ''{0}'' in relation with external id ''{1}''", rm.type,externalRelationId)
     510                    );
     511
     512                if (primitive == null) {
     513                    if (rm.id <= 0)
     514                        // relation member refers to a primitive with a negative id which was not
     515                        // found in the data. This is always a data integrity problem and we abort
     516                        // with an exception
     517                        //
     518                        throw new IllegalDataException(
     519                                tr(
     520                                        "Relation with external id ''{0}'' refers to missing primitive with external id ''{1}''",
     521                                        externalRelationId,
     522                                        rm.id
     523                                )
     524                        );
     525
     526                    // member refers to OSM primitive which was not present in the parsed data
     527                    // -> create a new incomplete primitive and add it to the dataset
     528                    //
     529                    if (rm.type.equals("node")) {
     530                        primitive = new Node(rm.id);
     531                    } else if (rm.type.equals("way")) {
     532                        primitive = new Way(rm.id);
     533                    } else if (rm.type.equals("relation")) {
     534                        primitive = new Relation(rm.id);
     535                    } else {
     536                        // can't happen, we've been testing for valid member types
     537                        // at the beginning of this method
     538                        //
    423539                    }
    424                 } else if (emd.type.equals("way")) {
    425                     member = hm.get(emd.id);
    426                     if (member == null) {
    427                         member = findWay(emd.id);
    428                     }
    429                     if (member == null) {
    430                         member = new Way(emd.id);
    431                         ds.addPrimitive(member);
    432                     }
    433                 } else if (emd.type.equals("relation")) {
    434                     member = findRelation(emd.id);
    435                     if (member == null) {
    436                         member = new Relation(emd.id);
    437                         ds.addPrimitive(member);
    438                     }
    439                 } else {
    440                     throw new SAXException(tr("Unknown relation member type {0}", emd.type));
    441                 }
    442                 relationMembers.add(new RelationMember(emd.role, member));
    443             }
    444             en.setMembers(relationMembers);
    445         }
    446         hm = null;
     540                    ds.addPrimitive(primitive);
     541                }
     542                relationMembers.add(new RelationMember(rm.role, primitive));
     543            }
     544            relation.setMembers(relationMembers);
     545            ds.addPrimitive(relation);
     546        }
    447547    }
    448548
    449549    /**
    450550     * Parse the given input source and return the dataset.
    451      * @param ref The dataset that is search in for references first. If
    452      *      the Reference is not found here, Main.ds is searched and a copy of the
    453      *  element found there is returned.
    454      */
    455     public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws SAXException, IOException {
    456         return parseDataSetOsm(source, progressMonitor).ds;
    457     }
    458 
    459     public static OsmReader parseDataSetOsm(InputStream source, ProgressMonitor progressMonitor) throws SAXException, IOException {
     551     *
     552     * @param source the source input stream
     553     * @param progressMonitor  the progress monitor
     554     *
     555     * @return the dataset with the parsed data
     556     * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
     557     */
     558    public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
    460559        OsmReader reader = new OsmReader();
    461 
    462         // phase 1: Parse nodes and read in raw ways
    463         InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
    464560        try {
     561            progressMonitor.beginTask(tr("Prepare OSM data...", 2));
     562            progressMonitor.subTask(tr("Parsing OSM data..."));
     563            InputSource inputSource = new InputSource(new InputStreamReader(source, "UTF-8"));
    465564            SAXParserFactory.newInstance().newSAXParser().parse(inputSource, reader.new Parser());
    466         } catch (ParserConfigurationException e1) {
    467             e1.printStackTrace(); // broken SAXException chaining
    468             throw new SAXException(e1);
    469         }
    470 
    471         progressMonitor.beginTask(tr("Prepare OSM data...", 2));
    472         try {
    473             for (Node n : reader.nodes.values()) {
    474                 reader.ds.addPrimitive(n);
    475             }
    476 
    477565            progressMonitor.worked(1);
    478566
    479             try {
    480                 reader.createWays();
    481                 reader.createRelations();
    482             } catch (NumberFormatException e) {
    483                 e.printStackTrace();
    484                 throw new SAXException(tr("Ill-formed node id"));
    485             }
    486 
    487             // clear all negative ids (new to this file)
    488             for (OsmPrimitive o : reader.ds.allPrimitives())
    489                 if (o.id < 0) {
    490                     o.id = 0;
    491                 }
    492 
    493             return reader;
     567            progressMonitor.subTask(tr("Preparing data set..."));
     568            reader.processNodesAfterParsing();
     569            reader.processWaysAfterParsing();
     570            reader.processRelationsAfterParsing();
     571            progressMonitor.worked(1);
     572            return reader.getDataSet();
     573        } catch(IllegalDataException e) {
     574            throw e;
     575        } catch(ParserConfigurationException e) {
     576            throw new IllegalDataException(e.getMessage(), e);
     577        } catch(SAXException e) {
     578            throw new IllegalDataException(e.getMessage(), e);
     579        } catch(Exception e) {
     580            throw new IllegalDataException(e);
    494581        } finally {
    495582            progressMonitor.finishTask();
  • trunk/src/org/openstreetmap/josm/io/OsmServerBackreferenceReader.java

    r1811 r2070  
    5151        if (primitive == null)
    5252            throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "primitive"));
    53         if (primitive.id == 0)
    54             throw new IllegalArgumentException(tr("id parameter ''{0}'' > 0 required. Got {1}", "primitive", primitive.id));
    55         this.id = primitive.id;
     53        if (primitive.getId() == 0)
     54            throw new IllegalArgumentException(tr("id parameter ''{0}'' > 0 required. Got {1}", "primitive", primitive.getId()));
     55        this.id = primitive.getId();
    5656        this.primitiveType = OsmPrimitiveType.from(primitive);
    5757        this.readFull = false;
     
    222222            if (isReadFull() ||primitiveType.equals(OsmPrimitiveType.NODE)) {
    223223                for (Way way: waysToCheck) {
    224                     if (way.id > 0 && way.incomplete) {
    225                         OsmServerObjectReader reader = new OsmServerObjectReader(way.id, OsmPrimitiveType.from(way), true /* read full */);
     224                    if (way.getId() > 0 && way.incomplete) {
     225                        OsmServerObjectReader reader = new OsmServerObjectReader(way.getId(), OsmPrimitiveType.from(way), true /* read full */);
    226226                        DataSet wayDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
    227227                        MergeVisitor visitor = new MergeVisitor(ds, wayDs);
     
    233233                Collection<Relation> relationsToCheck  = new ArrayList<Relation>(ds.relations);
    234234                for (Relation relation: relationsToCheck) {
    235                     if (relation.id > 0 && relation.incomplete) {
    236                         OsmServerObjectReader reader = new OsmServerObjectReader(relation.id, OsmPrimitiveType.from(relation), true /* read full */);
     235                    if (relation.getId() > 0 && relation.incomplete) {
     236                        OsmServerObjectReader reader = new OsmServerObjectReader(relation.getId(), OsmPrimitiveType.from(relation), true /* read full */);
    237237                        DataSet wayDs = reader.parseOsm(progressMonitor.createSubTaskMonitor(1, false));
    238238                        MergeVisitor visitor = new MergeVisitor(ds, wayDs);
  • trunk/src/org/openstreetmap/josm/io/OsmServerObjectReader.java

    r1827 r2070  
    4646            if (in == null)
    4747                return null;
    48             final OsmReader osm = OsmReader.parseDataSetOsm(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
    49             final DataSet data = osm.getDs();
     48            final DataSet data = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
    5049            return data;
    5150        } catch(OsmTransferException e) {
  • trunk/src/org/openstreetmap/josm/io/OsmWriter.java

    r2025 r2070  
    1414import org.openstreetmap.josm.data.osm.Relation;
    1515import org.openstreetmap.josm.data.osm.RelationMember;
     16import org.openstreetmap.josm.data.osm.User;
    1617import org.openstreetmap.josm.data.osm.Way;
    1718import org.openstreetmap.josm.data.osm.visitor.Visitor;
     
    208209        // user and visible added with 0.4 API
    209210        if (osm.user != null) {
    210             out.print(" user='"+XmlWriter.encode(osm.user.name)+"'");
     211            if(osm.user.isLocalUser()) {
     212                out.print(" user='"+XmlWriter.encode(osm.user.getName())+"'");
     213            } else if (osm.user.isOsmUser()) {
     214                // uid added with 0.6
     215                out.print(" uid='"+ osm.user.getId()+"'");
     216                out.print(" user='"+XmlWriter.encode(osm.user.getName())+"'");
     217            }
    211218        }
    212219        out.print(" visible='"+osm.isVisible()+"'");
    213         if (osm.version != -1) {
    214             out.print(" version='"+osm.version+"'");
     220        if (osm.getVersion() != 0) {
     221            out.print(" version='"+osm.getVersion()+"'");
    215222        }
    216223        if (this.changeset != null && this.changeset.getId() != 0) {
  • trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java

    r2017 r2070  
    122122        if(pd.mainversion > AboutAction.getVersionNumber())
    123123        {
    124             int answer = new ExtendedDialog(Main.parent,
     124            ExtendedDialog dialog = new ExtendedDialog(
     125                    Main.parent,
    125126                    tr("Skip download"),
    126                     tr("JOSM version {0} required for plugin {1}.", pd.mainversion, pd.name),
    127                     new String[] {tr("Download Plugin"), tr("Skip Download")},
    128                     new String[] {"download.png", "cancel.png"}).getValue();
     127                    new String[] {tr("Download Plugin"), tr("Skip Download")}
     128            );
     129            dialog.setContent(tr("JOSM version {0} required for plugin {1}.", pd.mainversion, pd.name));
     130            dialog.setButtonIcons(new String[] {"download.png", "cancel.png"});
     131            dialog.showDialog();
     132            int answer = dialog.getValue();
    129133            if (answer != 1)
    130134                return false;
  • trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java

    r2017 r2070  
    171171                } catch (Throwable e) {
    172172                    e.printStackTrace();
    173 
    174                     int result = new ExtendedDialog(Main.parent,
     173                    ExtendedDialog dialog = new ExtendedDialog(
     174                            Main.parent,
    175175                            tr("Disable plugin"),
    176                             tr("Could not load plugin {0}. Delete from preferences?", info.name),
    177                             new String[] {tr("Disable plugin"), tr("Keep plugin")},
    178                             new String[] {"dialogs/delete.png", "cancel.png"}).getValue();
     176                            new String[] {tr("Disable plugin"), tr("Keep plugin")}
     177                    );
     178                    dialog.setContent(tr("Could not load plugin {0}. Delete from preferences?", info.name));
     179                    dialog.setButtonIcons( new String[] {"dialogs/delete.png", "cancel.png"});
     180                    dialog.showDialog();
     181                    int result = dialog.getValue();
    179182
    180183                    if(result == 1)
     
    278281
    279282        if (plugin != null) {
    280             int answer = new ExtendedDialog(Main.parent,
     283            ExtendedDialog dialog = new ExtendedDialog(
     284                    Main.parent,
    281285                    tr("Disable plugin"),
     286                    new String[] {tr("Disable plugin"), tr("Cancel")}
     287            );
     288            dialog.setButtonIcons(new String[] {"dialogs/delete.png", "cancel.png"});
     289            dialog.setContent(
     290                    tr("<html>") +
    282291                    tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.", plugin.info.name)
    283                     + "\n"
     292                    + "<br>"
    284293                    + (plugin.info.author != null
    285294                            ? tr("According to the information within the plugin, the author is {0}.", plugin.info.author)
    286295                                    : "")
    287                                     + "\n"
     296                                    + "<br>"
    288297                                    + tr("Try updating to the newest version of this plugin before reporting a bug.")
    289                                     + "\n"
    290                                     + tr("Should the plugin be disabled?"),
    291                                     new String[] {tr("Disable plugin"), tr("Cancel")},
    292                                     new String[] {"dialogs/delete.png", "cancel.png"}).getValue();
     298                                    + "<br>"
     299                                    + tr("Should the plugin be disabled?")
     300                                    + "</html>"
     301            );
     302            dialog.showDialog();
     303            int answer = dialog.getValue();
     304
    293305            if (answer == 1) {
    294306                List<String> plugins = new ArrayList<String>(Main.pref.getCollection("plugins", Collections.<String>emptyList()));
  • trunk/src/org/openstreetmap/josm/tools/WindowGeometry.java

    r2066 r2070  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.awt.Component;
    67import java.awt.Dimension;
     8import java.awt.Frame;
    79import java.awt.Point;
    810import java.awt.Toolkit;
     
    1012import java.util.regex.Matcher;
    1113import java.util.regex.Pattern;
     14
     15import javax.swing.JOptionPane;
    1216
    1317import org.openstreetmap.josm.Main;
     
    4347     * @return the geometry object
    4448     */
    45     static public WindowGeometry centerInWindow(Window parent, Dimension extent) {
     49    static public WindowGeometry centerInWindow(Component parent, Dimension extent) {
     50        Frame parentWindow = JOptionPane.getFrameForComponent(parent);
    4651        Point topLeft = new Point(
    47                 Math.max(0, (parent.getSize().width - extent.width) /2),
    48                 Math.max(0, (parent.getSize().height - extent.height) /2)
     52                Math.max(0, (parentWindow.getSize().width - extent.width) /2),
     53                Math.max(0, (parentWindow.getSize().height - extent.height) /2)
    4954        );
    50         topLeft.x += parent.getLocation().x;
    51         topLeft.y += parent.getLocation().y;
     55        topLeft.x += parentWindow.getLocation().x;
     56        topLeft.y += parentWindow.getLocation().y;
    5257        return new WindowGeometry(topLeft, extent);
    5358    }
Note: See TracChangeset for help on using the changeset viewer.