Changeset 4413 in josm


Ignore:
Timestamp:
Sep 10, 2011 10:56:24 AM (21 months ago)
Author:
bastiK
Message:

make osm reader more robust - don't mess up xml structure when there are unknown elements (see #6742)

File:
1 edited

Legend:

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

    r4389 r4413  
    55 
    66import java.io.InputStream; 
     7import java.io.InputStreamReader; 
    78import java.text.MessageFormat; 
    89import java.util.ArrayList; 
     
    1213import java.util.List; 
    1314import java.util.Map; 
    14  
    15 import javax.xml.parsers.ParserConfigurationException; 
    16 import javax.xml.parsers.SAXParserFactory; 
     15import java.util.regex.Matcher; 
     16import java.util.regex.Pattern; 
     17 
     18import javax.xml.stream.XMLInputFactory; 
     19import javax.xml.stream.XMLStreamReader; 
     20import javax.xml.stream.XMLStreamConstants; 
     21import javax.xml.stream.XMLStreamException; 
     22import javax.xml.stream.Location; 
    1723 
    1824import org.openstreetmap.josm.data.Bounds; 
     
    3743import org.openstreetmap.josm.tools.CheckParameterUtil; 
    3844import org.openstreetmap.josm.tools.DateUtils; 
    39 import org.xml.sax.Attributes; 
    40 import org.xml.sax.InputSource; 
    41 import org.xml.sax.Locator; 
    42 import org.xml.sax.SAXException; 
    43 import org.xml.sax.SAXParseException; 
    44 import org.xml.sax.helpers.DefaultHandler; 
    4545 
    4646/** 
    4747 * Parser for the Osm Api. Read from an input stream and construct a dataset out of it. 
    4848 * 
     49 * For each xml element, there is a dedicated method. 
     50 * The XMLStreamReader cursor points to the start of the element, when the method is 
     51 * entered, and it must point to the end of the same element, when it is exited. 
    4952 */ 
    5053public class OsmReader { 
    51     /** 
    52      * The dataset to add parsed objects to. 
    53      */ 
    54     private DataSet ds = new DataSet(); 
    55  
    56     /** 
    57      * Replies the parsed data set 
    58      * 
    59      * @return the parsed data set 
    60      */ 
    61     public DataSet getDataSet() { 
    62         return ds; 
    63     } 
    64  
    65     /** the map from external ids to read OsmPrimitives. External ids are 
    66      * longs too, but in contrast to internal ids negative values are used 
    67      * to identify primitives unknown to the OSM server 
    68      */ 
    69     private Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 
    70  
    71     /** 
    72      * constructor (for private use only) 
    73      * 
    74      * @see #parseDataSet(InputStream, DataSet, ProgressMonitor) 
    75      * @see #parseDataSetOsm(InputStream, DataSet, ProgressMonitor) 
    76      */ 
    77     private OsmReader() { 
    78         externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 
    79     } 
    8054 
    8155    /** 
     
    9064 
    9165    /** 
     66     * The dataset to add parsed objects to. 
     67     */ 
     68    private DataSet ds = new DataSet(); 
     69 
     70    private XMLStreamReader parser; 
     71 
     72    /** the map from external ids to read OsmPrimitives. External ids are 
     73     * longs too, but in contrast to internal ids negative values are used 
     74     * to identify primitives unknown to the OSM server 
     75     */ 
     76    private Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 
     77 
     78    /** 
    9279     * Data structure for the remaining way objects 
    9380     */ 
     
    9986    private Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>(); 
    10087 
    101     private class Parser extends DefaultHandler { 
    102         private Locator locator; 
     88    /** 
     89     * constructor (for private use only) 
     90     * 
     91     * @see #parseDataSet(InputStream, DataSet, ProgressMonitor) 
     92     */ 
     93    private OsmReader() { 
     94        externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); 
     95    } 
     96 
     97    public void setParser(XMLStreamReader parser) { 
     98        this.parser = parser; 
     99    } 
     100 
     101    /** 
     102     * Replies the parsed data set 
     103     * 
     104     * @return the parsed data set 
     105     */ 
     106    public DataSet getDataSet() { 
     107        return ds; 
     108    } 
     109 
     110    protected void throwException(String msg) throws XMLStreamException { 
     111        throw new OsmParsingException(msg, parser.getLocation()); 
     112    } 
     113 
     114    public void parse() throws XMLStreamException { 
     115        int event = parser.getEventType(); 
     116        while (true) { 
     117            if (event == XMLStreamConstants.START_ELEMENT) { 
     118                if (parser.getLocalName().equals("osm") || parser.getLocalName().equals("osmChange")) { 
     119                    parseOsm(); 
     120                } else { 
     121                    parseUnkown(); 
     122                } 
     123            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     124                return; 
     125            } 
     126            if (parser.hasNext()) { 
     127                event = parser.next(); 
     128            } else { 
     129                break; 
     130            } 
     131        } 
     132        parser.close(); 
     133    } 
     134 
     135    private void parseOsm() throws XMLStreamException { 
     136        String v = parser.getAttributeValue(null, "version"); 
     137        if (v == null) { 
     138            throwException(tr("Missing mandatory attribute ''{0}''.", "version")); 
     139        } 
     140        if (!(v.equals("0.5") || v.equals("0.6"))) { 
     141            throwException(tr("Unsupported version: {0}", v)); 
     142        } 
     143        String generator = parser.getAttributeValue(null, "generator"); 
     144        ds.setVersion(v); 
     145        while (true) { 
     146            int event = parser.next(); 
     147            if (event == XMLStreamConstants.START_ELEMENT) { 
     148                if (parser.getLocalName().equals("bounds")) { 
     149                    parseBounds(generator); 
     150                } else if (parser.getLocalName().equals("node")) { 
     151                    parseNode(); 
     152                } else if (parser.getLocalName().equals("way")) { 
     153                    parseWay(); 
     154                } else if (parser.getLocalName().equals("relation")) { 
     155                    parseRelation(); 
     156                } else { 
     157                    parseUnkown(); 
     158                } 
     159            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     160                return; 
     161            } 
     162        } 
     163    } 
     164 
     165    private void parseBounds(String generator) throws XMLStreamException { 
     166        String minlon = parser.getAttributeValue(null, "minlon"); 
     167        String minlat = parser.getAttributeValue(null, "minlat"); 
     168        String maxlon = parser.getAttributeValue(null, "maxlon"); 
     169        String maxlat = parser.getAttributeValue(null, "maxlat"); 
     170        String origin = parser.getAttributeValue(null, "origin"); 
     171        if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 
     172            if (origin == null) { 
     173                origin = generator; 
     174            } 
     175            Bounds bounds = new Bounds( 
     176                    Double.parseDouble(minlat), Double.parseDouble(minlon), 
     177                    Double.parseDouble(maxlat), Double.parseDouble(maxlon)); 
     178            if (bounds.isOutOfTheWorld()) { 
     179                Bounds copy = new Bounds(bounds); 
     180                bounds.normalize(); 
     181                System.out.println("Bbox " + copy + " is out of the world, normalized to " + bounds); 
     182            } 
     183            DataSource src = new DataSource(bounds, origin); 
     184            ds.dataSources.add(src); 
     185        } else { 
     186            throwException(tr( 
     187                    "Missing mandatory attributes on element ''bounds''. Got minlon=''{0}'',minlat=''{1}'',maxlon=''{3}'',maxlat=''{4}'', origin=''{5}''.", 
     188                    minlon, minlat, maxlon, maxlat, origin 
     189            )); 
     190        } 
     191        jumpToEnd(); 
     192    } 
     193 
     194    private void parseNode() throws XMLStreamException { 
     195        NodeData nd = new NodeData(); 
     196        nd.setCoor(new LatLon(Double.parseDouble(parser.getAttributeValue(null, "lat")), Double.parseDouble(parser.getAttributeValue(null, "lon")))); 
     197        readCommon(nd); 
     198        Node n = new Node(nd.getId(), nd.getVersion()); 
     199        n.setVisible(nd.isVisible()); 
     200        n.load(nd); 
     201        externalIdMap.put(nd.getPrimitiveId(), n); 
     202        while (true) { 
     203            int event = parser.next(); 
     204            if (event == XMLStreamConstants.START_ELEMENT) { 
     205                if (parser.getLocalName().equals("tag")) { 
     206                    parseTag(n); 
     207                } else { 
     208                    parseUnkown(); 
     209                } 
     210            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     211                return; 
     212            } 
     213        } 
     214    } 
     215 
     216    private void parseWay() throws XMLStreamException { 
     217        WayData wd = new WayData(); 
     218        readCommon(wd); 
     219        Way w = new Way(wd.getId(), wd.getVersion()); 
     220        w.setVisible(wd.isVisible()); 
     221        w.load(wd); 
     222        externalIdMap.put(wd.getPrimitiveId(), w); 
     223 
     224        Collection<Long> nodeIds = new ArrayList<Long>(); 
     225        while (true) { 
     226            int event = parser.next(); 
     227            if (event == XMLStreamConstants.START_ELEMENT) { 
     228                if (parser.getLocalName().equals("nd")) { 
     229                    nodeIds.add(parseWayNode(w)); 
     230                } else if (parser.getLocalName().equals("tag")) { 
     231                    parseTag(w); 
     232                } else { 
     233                    parseUnkown(); 
     234                } 
     235            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     236                break; 
     237            } 
     238        } 
     239        if (w.isDeleted() && nodeIds.size() > 0) { 
     240            System.out.println(tr("Deleted way {0} contains nodes", w.getUniqueId())); 
     241            nodeIds = new ArrayList<Long>(); 
     242        } 
     243        ways.put(wd.getUniqueId(), nodeIds); 
     244    } 
     245 
     246    private long parseWayNode(Way w) throws XMLStreamException { 
     247        if (parser.getAttributeValue(null, "ref") == null) { 
     248            throwException( 
     249                    tr("Missing mandatory attribute ''{0}'' on <nd> of way {1}.", "ref", w.getUniqueId()) 
     250            ); 
     251        } 
     252        long id = getLong("ref"); 
     253        if (id == 0) { 
     254            throwException( 
     255                    tr("Illegal value of attribute ''ref'' of element <nd>. Got {0}.", id) 
     256            ); 
     257        } 
     258        jumpToEnd(); 
     259        return id; 
     260    } 
     261 
     262    private void parseRelation() throws XMLStreamException { 
     263        RelationData rd = new RelationData(); 
     264        readCommon(rd); 
     265        Relation r = new Relation(rd.getId(), rd.getVersion()); 
     266        r.setVisible(rd.isVisible()); 
     267        r.load(rd); 
     268        externalIdMap.put(rd.getPrimitiveId(), r); 
     269 
     270        Collection<RelationMemberData> members = new ArrayList<RelationMemberData>(); 
     271        while (true) { 
     272            int event = parser.next(); 
     273            if (event == XMLStreamConstants.START_ELEMENT) { 
     274                if (parser.getLocalName().equals("member")) { 
     275                    members.add(parseRelationMember(r)); 
     276                } else if (parser.getLocalName().equals("tag")) { 
     277                    parseTag(r); 
     278                } else { 
     279                    parseUnkown(); 
     280                } 
     281            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     282                break; 
     283            } 
     284        } 
     285        if (r.isDeleted() && members.size() > 0) { 
     286            System.out.println(tr("Deleted relation {0} contains members", r.getUniqueId())); 
     287            members = new ArrayList<RelationMemberData>(); 
     288        } 
     289        relations.put(rd.getUniqueId(), members); 
     290    } 
     291 
     292    private RelationMemberData parseRelationMember(Relation r) throws XMLStreamException { 
     293        RelationMemberData emd = new RelationMemberData(); 
     294        String value = parser.getAttributeValue(null, "ref"); 
     295        if (value == null) { 
     296            throwException(tr("Missing attribute ''ref'' on member in relation {0}.",r.getUniqueId())); 
     297        } 
     298        try { 
     299            emd.id = Long.parseLong(value); 
     300        } catch(NumberFormatException e) { 
     301            throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(r.getUniqueId()),value)); 
     302        } 
     303        value = parser.getAttributeValue(null, "type"); 
     304        if (value == null) { 
     305            throwException(tr("Missing attribute ''type'' on member {0} in relation {1}.", Long.toString(emd.id), Long.toString(r.getUniqueId()))); 
     306        } 
     307        try { 
     308            emd.type = OsmPrimitiveType.fromApiTypeName(value); 
     309        } catch(IllegalArgumentException e) { 
     310            throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", Long.toString(emd.id), Long.toString(r.getUniqueId()), value)); 
     311        } 
     312        value = parser.getAttributeValue(null, "role"); 
     313        emd.role = value; 
     314 
     315        if (emd.id == 0) { 
     316            throwException(tr("Incomplete <member> specification with ref=0")); 
     317        } 
     318        jumpToEnd(); 
     319        return emd; 
     320    } 
     321 
     322    private void parseTag(OsmPrimitive osm) throws XMLStreamException { 
     323        String key = parser.getAttributeValue(null, "k"); 
     324        String value = parser.getAttributeValue(null, "v"); 
     325        if (key == null || value == null) { 
     326            throwException(tr("Missing key or value attribute in tag.")); 
     327        } 
     328        osm.put(key.intern(), value.intern()); 
     329        jumpToEnd(); 
     330    } 
     331 
     332    /** 
     333     * When cursor is at the start of an element, moves it to the end tag of that element. 
     334     * Nested content is skipped. 
     335     * 
     336     * This is basically the same code as parseUnkown(), except for the warnings, which 
     337     * are displayed for inner elements and not at top level. 
     338     */ 
     339    private void jumpToEnd() throws XMLStreamException { 
     340        while (true) { 
     341            int event = parser.next(); 
     342            if (event == XMLStreamConstants.START_ELEMENT) { 
     343                parseUnkown(); 
     344            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     345                return; 
     346            } 
     347        } 
     348    } 
     349 
     350    private void parseUnkown() throws XMLStreamException { 
     351        parseUnkown(true); 
     352    } 
     353 
     354    private void parseUnkown(boolean printWarning) throws XMLStreamException { 
     355        if (printWarning) { 
     356            System.out.println(tr("Undefined element ''{0}'' found in input stream. Skipping.", parser.getLocalName())); 
     357        } 
     358        while (true) { 
     359            int event = parser.next(); 
     360            if (event == XMLStreamConstants.START_ELEMENT) { 
     361                parseUnkown(false); /* no more warning for inner elements */ 
     362            } else if (event == XMLStreamConstants.END_ELEMENT) { 
     363                return; 
     364            } 
     365        } 
     366    } 
     367 
     368    private User createUser(String uid, String name) throws XMLStreamException { 
     369        if (uid == null) { 
     370            if (name == null) 
     371                return null; 
     372            return User.createLocalUser(name); 
     373        } 
     374        try { 
     375            long id = Long.parseLong(uid); 
     376            return User.createOsmUser(id, name); 
     377        } catch(NumberFormatException e) { 
     378            throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid)); 
     379        } 
     380        return null; 
     381    } 
     382 
     383    /** 
     384     * Read out the common attributes and put them into current OsmPrimitive. 
     385     */ 
     386    private void readCommon(PrimitiveData current) throws XMLStreamException { 
     387        current.setId(getLong("id")); 
     388        if (current.getUniqueId() == 0) { 
     389            throwException(tr("Illegal object with ID=0.")); 
     390        } 
     391 
     392        String time = parser.getAttributeValue(null, "timestamp"); 
     393        if (time != null && time.length() != 0) { 
     394            current.setTimestamp(DateUtils.fromString(time)); 
     395        } 
     396 
     397        // user attribute added in 0.4 API 
     398        String user = parser.getAttributeValue(null, "user"); 
     399        // uid attribute added in 0.6 API 
     400        String uid = parser.getAttributeValue(null, "uid"); 
     401        current.setUser(createUser(uid, user)); 
     402 
     403        // visible attribute added in 0.4 API 
     404        String visible = parser.getAttributeValue(null, "visible"); 
     405        if (visible != null) { 
     406            current.setVisible(Boolean.parseBoolean(visible)); 
     407        } 
     408 
     409        String versionString = parser.getAttributeValue(null, "version"); 
     410        int version = 0; 
     411        if (versionString != null) { 
     412            try { 
     413                version = Integer.parseInt(versionString); 
     414            } catch(NumberFormatException e) { 
     415                throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString)); 
     416            } 
     417            if (ds.getVersion().equals("0.6")){ 
     418                if (version <= 0 && current.getUniqueId() > 0) { 
     419                    throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString)); 
     420                } else if (version < 0 && current.getUniqueId() <= 0) { 
     421                    System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.6")); 
     422                    version = 0; 
     423                } 
     424            } else if (ds.getVersion().equals("0.5")) { 
     425                if (version <= 0 && current.getUniqueId() > 0) { 
     426                    System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5")); 
     427                    version = 1; 
     428                } else if (version < 0 && current.getUniqueId() <= 0) { 
     429                    System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.5")); 
     430                    version = 0; 
     431                } 
     432            } else { 
     433                // should not happen. API version has been checked before 
     434                throwException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion())); 
     435            } 
     436        } else { 
     437            // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 
     438            // 
     439            if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.6")) { 
     440                throwException(tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId()))); 
     441            } else if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) { 
     442                // default version in 0.5 files for existing primitives 
     443                System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5")); 
     444                version= 1; 
     445            } else if (current.getUniqueId() <= 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) { 
     446                // default version in 0.5 files for new primitives, no warning necessary. This is 
     447                // (was) legal in API 0.5 
     448                version= 0; 
     449            } 
     450        } 
     451        current.setVersion(version); 
     452 
     453        String action = parser.getAttributeValue(null, "action"); 
     454        if (action == null) { 
     455            // do nothing 
     456        } else if (action.equals("delete")) { 
     457            current.setDeleted(true); 
     458            current.setModified(current.isVisible()); 
     459        } else if (action.equals("modify")) { 
     460            current.setModified(true); 
     461        } 
     462 
     463        String v = parser.getAttributeValue(null, "changeset"); 
     464        if (v == null) { 
     465            current.setChangesetId(0); 
     466        } else { 
     467            try { 
     468                current.setChangesetId(Integer.parseInt(v)); 
     469            } catch(NumberFormatException e) { 
     470                if (current.getUniqueId() <= 0) { 
     471                    // for a new primitive we just log a warning 
     472                    System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId())); 
     473                    current.setChangesetId(0); 
     474                } else { 
     475                    // for an existing primitive this is a problem 
     476                    throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 
     477                } 
     478            } 
     479            if (current.getChangesetId() <=0) { 
     480                if (current.getUniqueId() <= 0) { 
     481                    // for a new primitive we just log a warning 
     482                    System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId())); 
     483                    current.setChangesetId(0); 
     484                } else { 
     485                    // for an existing primitive this is a problem 
     486                    throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 
     487                } 
     488            } 
     489        } 
     490    } 
     491 
     492    private long getLong(String name) throws XMLStreamException { 
     493        String value = parser.getAttributeValue(null, name); 
     494        if (value == null) { 
     495            throwException(tr("Missing required attribute ''{0}''.",name)); 
     496        } 
     497        try { 
     498            return Long.parseLong(value); 
     499        } catch(NumberFormatException e) { 
     500            throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.",name, value)); 
     501        } 
     502        return 0; // should not happen 
     503    } 
     504 
     505    private static class OsmParsingException extends XMLStreamException { 
     506        public OsmParsingException() { 
     507            super(); 
     508        } 
     509 
     510        public OsmParsingException(String msg) { 
     511            super(msg); 
     512        } 
     513 
     514        public OsmParsingException(String msg, Location location) { 
     515            super(msg); /* cannot use super(msg, location) because it messes with the message preventing localization */ 
     516            this.location = location; 
     517        } 
     518 
     519        public OsmParsingException(String msg, Location location, Throwable th) { 
     520            super(msg, th); 
     521            this.location = location; 
     522        } 
     523 
     524        public OsmParsingException(String msg, Throwable th) { 
     525            super(msg, th); 
     526        } 
     527 
     528        public OsmParsingException(Throwable th) { 
     529            super(th); 
     530        } 
    103531 
    104532        @Override 
    105         public void setDocumentLocator(Locator locator) { 
    106             this.locator = locator; 
    107         } 
    108  
    109         protected void throwException(String msg) throws OsmDataParsingException{ 
    110             throw new OsmDataParsingException(msg).rememberLocation(locator); 
    111         } 
    112         /** 
    113          * The current osm primitive to be read. 
    114          */ 
    115         private OsmPrimitive currentPrimitive; 
    116         private long currentExternalId; 
    117         private String generator; 
    118  
    119         @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 
    120  
    121             try { 
    122                 if (qName.equals("osm") || qName.equals("osmChange")) { 
    123                     if (atts == null) { 
    124                         throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "version", qName)); 
    125                     } 
    126                     String v = atts.getValue("version"); 
    127                     if (v == null) { 
    128                         throwException(tr("Missing mandatory attribute ''{0}''.", "version")); 
    129                     } 
    130                     if (!(v.equals("0.5") || v.equals("0.6"))) { 
    131                         throwException(tr("Unsupported version: {0}", v)); 
    132                     } 
    133                     // save generator attribute for later use when creating DataSource objects 
    134                     generator = atts.getValue("generator"); 
    135                     ds.setVersion(v); 
    136  
    137                 } else if (qName.equals("bounds")) { 
    138                     // new style bounds. 
    139                     String minlon = atts.getValue("minlon"); 
    140                     String minlat = atts.getValue("minlat"); 
    141                     String maxlon = atts.getValue("maxlon"); 
    142                     String maxlat = atts.getValue("maxlat"); 
    143                     String origin = atts.getValue("origin"); 
    144                     if (minlon != null && maxlon != null && minlat != null && maxlat != null) { 
    145                         if (origin == null) { 
    146                             origin = generator; 
    147                         } 
    148                         Bounds bounds = new Bounds( 
    149                                 Double.parseDouble(minlat), Double.parseDouble(minlon), 
    150                                 Double.parseDouble(maxlat), Double.parseDouble(maxlon)); 
    151                         if (bounds.isOutOfTheWorld()) { 
    152                             Bounds copy = new Bounds(bounds); 
    153                             bounds.normalize(); 
    154                             System.out.println("Bbox " + copy + " is out of the world, normalized to " + bounds); 
    155                         } 
    156                         DataSource src = new DataSource(bounds, origin); 
    157                         ds.dataSources.add(src); 
    158                     } else { 
    159                         throwException(tr( 
    160                                 "Missing mandatory attributes on element ''bounds''. Got minlon=''{0}'',minlat=''{1}'',maxlon=''{3}'',maxlat=''{4}'', origin=''{5}''.", 
    161                                 minlon, minlat, maxlon, maxlat, origin 
    162                         )); 
    163                     } 
    164  
    165                     // ---- PARSING NODES AND WAYS ---- 
    166  
    167                 } else if (qName.equals("node")) { 
    168                     NodeData nd = new NodeData(); 
    169                     nd.setCoor(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon"))); 
    170                     readCommon(atts, nd); 
    171                     Node n = new Node(nd.getId(), nd.getVersion()); 
    172                     n.setVisible(nd.isVisible()); 
    173                     n.load(nd); 
    174                     externalIdMap.put(nd.getPrimitiveId(), n); 
    175                     currentPrimitive = n; 
    176                     currentExternalId = nd.getUniqueId(); 
    177                 } else if (qName.equals("way")) { 
    178                     WayData wd = new WayData(); 
    179                     readCommon(atts, wd); 
    180                     Way w = new Way(wd.getId(), wd.getVersion()); 
    181                     w.setVisible(wd.isVisible()); 
    182                     w.load(wd); 
    183                     externalIdMap.put(wd.getPrimitiveId(), w); 
    184                     ways.put(wd.getUniqueId(), new ArrayList<Long>()); 
    185                     currentPrimitive = w; 
    186                     currentExternalId = wd.getUniqueId(); 
    187                 } else if (qName.equals("nd")) { 
    188                     Collection<Long> list = ways.get(currentExternalId); 
    189                     if (list == null) { 
    190                         throwException( 
    191                                 tr("Found XML element <nd> not as direct child of element <way>.") 
    192                         ); 
    193                     } 
    194                     if (atts.getValue("ref") == null) { 
    195                         throwException( 
    196                                 tr("Missing mandatory attribute ''{0}'' on <nd> of way {1}.", "ref", currentPrimitive.getUniqueId()) 
    197                         ); 
    198                     } 
    199                     long id = getLong(atts, "ref"); 
    200                     if (id == 0) { 
    201                         throwException( 
    202                                 tr("Illegal value of attribute ''ref'' of element <nd>. Got {0}.", id) 
    203                         ); 
    204                     } 
    205                     if (currentPrimitive.isDeleted()) { 
    206                         System.out.println(tr("Deleted way {0} contains nodes", currentPrimitive.getUniqueId())); 
    207                     } else { 
    208                         list.add(id); 
    209                     } 
    210  
    211                     // ---- PARSING RELATIONS ---- 
    212  
    213                 } else if (qName.equals("relation")) { 
    214                     RelationData rd = new RelationData(); 
    215                     readCommon(atts, rd); 
    216                     Relation r = new Relation(rd.getId(), rd.getVersion()); 
    217                     r.setVisible(rd.isVisible()); 
    218                     r.load(rd); 
    219                     externalIdMap.put(rd.getPrimitiveId(), r); 
    220                     relations.put(rd.getUniqueId(), new LinkedList<RelationMemberData>()); 
    221                     currentPrimitive = r; 
    222                     currentExternalId = rd.getUniqueId(); 
    223                 } else if (qName.equals("member")) { 
    224                     Collection<RelationMemberData> list = relations.get(currentExternalId); 
    225                     if (list == null) { 
    226                         throwException( 
    227                                 tr("Found XML element <member> not as direct child of element <relation>.") 
    228                         ); 
    229                     } 
    230                     RelationMemberData emd = new RelationMemberData(); 
    231                     String value = atts.getValue("ref"); 
    232                     if (value == null) { 
    233                         throwException(tr("Missing attribute ''ref'' on member in relation {0}.",currentPrimitive.getUniqueId())); 
    234                     } 
    235                     try { 
    236                         emd.id = Long.parseLong(value); 
    237                     } catch(NumberFormatException e) { 
    238                         throwException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}", Long.toString(currentPrimitive.getUniqueId()),value)); 
    239                     } 
    240                     value = atts.getValue("type"); 
    241                     if (value == null) { 
    242                         throwException(tr("Missing attribute ''type'' on member {0} in relation {1}.", Long.toString(emd.id), Long.toString(currentPrimitive.getUniqueId()))); 
    243                     } 
    244                     try { 
    245                         emd.type = OsmPrimitiveType.fromApiTypeName(value); 
    246                     } catch(IllegalArgumentException e) { 
    247                         throwException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.", Long.toString(emd.id), Long.toString(currentPrimitive.getUniqueId()), value)); 
    248                     } 
    249                     value = atts.getValue("role"); 
    250                     emd.role = value; 
    251  
    252                     if (emd.id == 0) { 
    253                         throwException(tr("Incomplete <member> specification with ref=0")); 
    254                     } 
    255  
    256                     if (currentPrimitive.isDeleted()) { 
    257                         System.out.println(tr("Deleted relation {0} contains members", currentPrimitive.getUniqueId())); 
    258                     } else { 
    259                         list.add(emd); 
    260                     } 
    261  
    262                     // ---- PARSING TAGS (applicable to all objects) ---- 
    263  
    264                 } else if (qName.equals("tag")) { 
    265                     String key = atts.getValue("k"); 
    266                     String value = atts.getValue("v"); 
    267                     if (key == null || value == null) { 
    268                         throwException(tr("Missing key or value attribute in tag.")); 
    269                     } 
    270                     if (currentPrimitive != null) { 
    271                         currentPrimitive.put(key.intern(), value.intern()); 
    272                     } 
    273                 } else { 
    274                     System.out.println(tr("Undefined element ''{0}'' found in input stream. Skipping.", qName)); 
    275                 } 
    276             } catch (Exception e) { 
    277                 throw new SAXParseException(e.getMessage(), locator, e); 
    278             } 
    279         } 
    280  
    281         private double getDouble(Attributes atts, String value) { 
    282             return Double.parseDouble(atts.getValue(value)); 
    283         } 
    284  
    285         private User createUser(String uid, String name) throws SAXException { 
    286             if (uid == null) { 
    287                 if (name == null) 
    288                     return null; 
    289                 return User.createLocalUser(name); 
    290             } 
    291             try { 
    292                 long id = Long.parseLong(uid); 
    293                 return User.createOsmUser(id, name); 
    294             } catch(NumberFormatException e) { 
    295                 throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid)); 
    296             } 
    297             return null; 
    298         } 
    299         /** 
    300          * Read out the common attributes from atts and put them into this.current. 
    301          */ 
    302         void readCommon(Attributes atts, PrimitiveData current) throws SAXException { 
    303             current.setId(getLong(atts, "id")); 
    304             if (current.getUniqueId() == 0) { 
    305                 throwException(tr("Illegal object with ID=0.")); 
    306             } 
    307  
    308             String time = atts.getValue("timestamp"); 
    309             if (time != null && time.length() != 0) { 
    310                 current.setTimestamp(DateUtils.fromString(time)); 
    311             } 
    312  
    313             // user attribute added in 0.4 API 
    314             String user = atts.getValue("user"); 
    315             // uid attribute added in 0.6 API 
    316             String uid = atts.getValue("uid"); 
    317             current.setUser(createUser(uid, user)); 
    318  
    319             // visible attribute added in 0.4 API 
    320             String visible = atts.getValue("visible"); 
    321             if (visible != null) { 
    322                 current.setVisible(Boolean.parseBoolean(visible)); 
    323             } 
    324  
    325             String versionString = atts.getValue("version"); 
    326             int version = 0; 
    327             if (versionString != null) { 
    328                 try { 
    329                     version = Integer.parseInt(versionString); 
    330                 } catch(NumberFormatException e) { 
    331                     throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString)); 
    332                 } 
    333                 if (ds.getVersion().equals("0.6")){ 
    334                     if (version <= 0 && current.getUniqueId() > 0) { 
    335                         throwException(tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.", Long.toString(current.getUniqueId()), versionString)); 
    336                     } else if (version < 0 && current.getUniqueId() <= 0) { 
    337                         System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.6")); 
    338                         version = 0; 
    339                     } 
    340                 } else if (ds.getVersion().equals("0.5")) { 
    341                     if (version <= 0 && current.getUniqueId() > 0) { 
    342                         System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5")); 
    343                         version = 1; 
    344                     } else if (version < 0 && current.getUniqueId() <= 0) { 
    345                         System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 0, "0.5")); 
    346                         version = 0; 
    347                     } 
    348                 } else { 
    349                     // should not happen. API version has been checked before 
    350                     throwException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion())); 
    351                 } 
    352             } else { 
    353                 // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6 
    354                 // 
    355                 if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.6")) { 
    356                     throwException(tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId()))); 
    357                 } else if (current.getUniqueId() > 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) { 
    358                     // default version in 0.5 files for existing primitives 
    359                     System.out.println(tr("WARNING: Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.", current.getUniqueId(), version, 1, "0.5")); 
    360                     version= 1; 
    361                 } else if (current.getUniqueId() <= 0 && ds.getVersion() != null && ds.getVersion().equals("0.5")) { 
    362                     // default version in 0.5 files for new primitives, no warning necessary. This is 
    363                     // (was) legal in API 0.5 
    364                     version= 0; 
    365                 } 
    366             } 
    367             current.setVersion(version); 
    368  
    369             String action = atts.getValue("action"); 
    370             if (action == null) { 
    371                 // do nothing 
    372             } else if (action.equals("delete")) { 
    373                 current.setDeleted(true); 
    374                 current.setModified(current.isVisible()); 
    375             } else if (action.equals("modify")) { 
    376                 current.setModified(true); 
    377             } 
    378  
    379             String v = atts.getValue("changeset"); 
    380             if (v == null) { 
    381                 current.setChangesetId(0); 
    382             } else { 
    383                 try { 
    384                     current.setChangesetId(Integer.parseInt(v)); 
    385                 } catch(NumberFormatException e) { 
    386                     if (current.getUniqueId() <= 0) { 
    387                         // for a new primitive we just log a warning 
    388                         System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId())); 
    389                         current.setChangesetId(0); 
    390                     } else { 
    391                         // for an existing primitive this is a problem 
    392                         throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 
    393                     } 
    394                 } 
    395                 if (current.getChangesetId() <=0) { 
    396                     if (current.getUniqueId() <= 0) { 
    397                         // for a new primitive we just log a warning 
    398                         System.out.println(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.", v, current.getUniqueId())); 
    399                         current.setChangesetId(0); 
    400                     } else { 
    401                         // for an existing primitive this is a problem 
    402                         throwException(tr("Illegal value for attribute ''changeset''. Got {0}.", v)); 
    403                     } 
    404                 } 
    405             } 
    406         } 
    407  
    408         private long getLong(Attributes atts, String name) throws SAXException { 
    409             String value = atts.getValue(name); 
    410             if (value == null) { 
    411                 throwException(tr("Missing required attribute ''{0}''.",name)); 
    412             } 
    413             try { 
    414                 return Long.parseLong(value); 
    415             } catch(NumberFormatException e) { 
    416                 throwException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.",name, value)); 
    417             } 
    418             return 0; // should not happen 
     533        public String getMessage() { 
     534            String msg = super.getMessage(); 
     535            if (msg == null) { 
     536                msg = getClass().getName(); 
     537            } 
     538            if (getLocation() == null) 
     539                return msg; 
     540            msg = msg + " " + tr("(at line {0}, column {1})", getLocation().getLineNumber(), getLocation().getColumnNumber()); 
     541            return msg; 
     542        } 
     543    } 
     544 
     545    /** 
     546     * Processes the parsed nodes after parsing. Just adds them to 
     547     * the dataset 
     548     * 
     549     */ 
     550    protected void processNodesAfterParsing() { 
     551        for (OsmPrimitive primitive: externalIdMap.values()) { 
     552            if (primitive instanceof Node) { 
     553                this.ds.addPrimitive(primitive); 
     554            } 
    419555        } 
    420556    } 
     
    462598 
    463599    /** 
    464      * Processes the parsed nodes after parsing. Just adds them to 
    465      * the dataset 
    466      * 
    467      */ 
    468     protected void processNodesAfterParsing() { 
    469         for (OsmPrimitive primitive: externalIdMap.values()) { 
    470             if (primitive instanceof Node) { 
    471                 this.ds.addPrimitive(primitive); 
    472             } 
    473         } 
    474     } 
    475  
    476     /** 
    477600     * Completes the parsed relations with its members. 
    478601     * 
     
    562685            progressMonitor.indeterminateSubTask(tr("Parsing OSM data...")); 
    563686 
    564             InputSource inputSource = new InputSource(UTFInputStreamReader.create(source, "UTF-8")); 
    565             SAXParserFactory.newInstance().newSAXParser().parse(inputSource, reader.new Parser()); 
     687            InputStreamReader ir = UTFInputStreamReader.create(source, "UTF-8"); 
     688            XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(ir); 
     689            reader.setParser(parser); 
     690            reader.parse(); 
    566691            progressMonitor.worked(1); 
    567692 
     
    579704        } catch(IllegalDataException e) { 
    580705            throw e; 
    581         } catch(ParserConfigurationException e) { 
     706        } catch(OsmParsingException e) { 
    582707            throw new IllegalDataException(e.getMessage(), e); 
    583         } catch (SAXParseException e) { 
    584             throw new IllegalDataException(tr("Line {0} column {1}: ", e.getLineNumber(), e.getColumnNumber()) + e.getMessage(), e); 
    585         } catch(SAXException e) { 
    586             throw new IllegalDataException(e.getMessage(), e); 
     708        } catch(XMLStreamException e) { 
     709            String msg = e.getMessage(); 
     710            Pattern p = Pattern.compile("Message: (.+)"); 
     711            Matcher m = p.matcher(msg); 
     712            if (m.find()) { 
     713                msg = m.group(1); 
     714            } 
     715            if (e.getLocation() != null) { 
     716                throw new IllegalDataException(tr("Line {0} column {1}: ", e.getLocation().getLineNumber(), e.getLocation().getColumnNumber()) + msg, e); 
     717            } else { 
     718                throw new IllegalDataException(msg, e); 
     719            } 
    587720        } catch(Exception e) { 
    588721            throw new IllegalDataException(e); 
Note: See TracChangeset for help on using the changeset viewer.