Ignore:
Timestamp:
2009-06-23T22:03:37+02:00 (17 years ago)
Author:
Gubaer
Message:

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

Location:
trunk/src/org/openstreetmap/josm/io
Files:
1 added
2 edited

Legend:

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

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

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