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

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/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.