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

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

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

File:
1 edited

Legend:

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

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