001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.Date;
007
008import org.openstreetmap.josm.data.coor.LatLon;
009import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
010import org.openstreetmap.josm.data.osm.RelationMemberData;
011import org.openstreetmap.josm.data.osm.User;
012import org.openstreetmap.josm.data.osm.history.HistoryNode;
013import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
014import org.openstreetmap.josm.data.osm.history.HistoryRelation;
015import org.openstreetmap.josm.data.osm.history.HistoryWay;
016import org.openstreetmap.josm.tools.date.DateUtils;
017import org.xml.sax.Attributes;
018import org.xml.sax.Locator;
019import org.xml.sax.SAXException;
020import org.xml.sax.helpers.DefaultHandler;
021
022/**
023 * Base class of {@link OsmChangesetContentParser} and {@link OsmHistoryReader} internal parsers.
024 * @since 6201
025 */
026public abstract class AbstractParser extends DefaultHandler {
027
028    /** the current primitive to be read */
029    protected HistoryOsmPrimitive currentPrimitive;
030    protected Locator locator;
031
032    @Override
033    public void setDocumentLocator(Locator locator) {
034        this.locator = locator;
035    }
036
037    protected abstract void throwException(String message) throws SAXException;
038
039    protected abstract void throwException(String message, Exception e) throws SAXException;
040
041    protected final long getMandatoryAttributeLong(Attributes attr, String name) throws SAXException {
042        String v = attr.getValue(name);
043        if (v == null) {
044            throwException(tr("Missing mandatory attribute ''{0}''.", name));
045        }
046        long l = 0L;
047        try {
048            l = Long.parseLong(v);
049        } catch (NumberFormatException e) {
050            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long. Got ''{1}''.", name, v), e);
051        }
052        if (l < 0) {
053            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long (>=0). Got ''{1}''.", name, v));
054        }
055        return l;
056    }
057
058    protected final Long getAttributeLong(Attributes attr, String name) throws SAXException {
059        String v = attr.getValue(name);
060        if (v == null)
061            return null;
062        Long l = 0L;
063        try {
064            l = Long.valueOf(v);
065        } catch (NumberFormatException e) {
066            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long. Got ''{1}''.", name, v), e);
067        }
068        if (l < 0) {
069            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long (>=0). Got ''{1}''.", name, v));
070        }
071        return l;
072    }
073
074    protected final Double getAttributeDouble(Attributes attr, String name) throws SAXException {
075        String v = attr.getValue(name);
076        if (v == null) {
077            return null;
078        }
079        double d = 0.0;
080        try {
081            d = Double.parseDouble(v);
082        } catch (NumberFormatException e) {
083            throwException(tr("Illegal value for attribute ''{0}'' of type double. Got ''{1}''.", name, v), e);
084        }
085        return d;
086    }
087
088    protected final String getMandatoryAttributeString(Attributes attr, String name) throws SAXException {
089        String v = attr.getValue(name);
090        if (v == null) {
091            throwException(tr("Missing mandatory attribute ''{0}''.", name));
092        }
093        return v;
094    }
095
096    protected boolean getMandatoryAttributeBoolean(Attributes attr, String name) throws SAXException {
097        String v = attr.getValue(name);
098        if (v == null) {
099            throwException(tr("Missing mandatory attribute ''{0}''.", name));
100        }
101        if ("true".equals(v)) return true;
102        if ("false".equals(v)) return false;
103        throwException(tr("Illegal value for mandatory attribute ''{0}'' of type boolean. Got ''{1}''.", name, v));
104        return false; // not reached
105    }
106
107    protected final HistoryOsmPrimitive createPrimitive(Attributes atts, OsmPrimitiveType type) throws SAXException {
108        long id = getMandatoryAttributeLong(atts, "id");
109        long version = getMandatoryAttributeLong(atts, "version");
110        long changesetId = getMandatoryAttributeLong(atts, "changeset");
111        boolean visible = getMandatoryAttributeBoolean(atts, "visible");
112
113        Long uid = getAttributeLong(atts, "uid");
114        String userStr = atts.getValue("user");
115        User user;
116        if (userStr != null) {
117            if (uid != null) {
118                user = User.createOsmUser(uid, userStr);
119                user.setPreferredName(userStr);
120            } else {
121                user = User.createLocalUser(userStr);
122            }
123        } else {
124            user = User.getAnonymous();
125        }
126
127        String v = getMandatoryAttributeString(atts, "timestamp");
128        Date timestamp = DateUtils.fromString(v);
129        HistoryOsmPrimitive primitive = null;
130        if (type.equals(OsmPrimitiveType.NODE)) {
131            Double lat = getAttributeDouble(atts, "lat");
132            Double lon = getAttributeDouble(atts, "lon");
133            LatLon coor = (lat != null && lon != null) ? new LatLon(lat, lon) : null;
134            primitive = new HistoryNode(id, version, visible, user, changesetId, timestamp, coor);
135
136        } else if (type.equals(OsmPrimitiveType.WAY)) {
137            primitive = new HistoryWay(id, version, visible, user, changesetId, timestamp);
138        } else if (type.equals(OsmPrimitiveType.RELATION)) {
139            primitive = new HistoryRelation(id, version, visible, user, changesetId, timestamp);
140        }
141        return primitive;
142    }
143
144    protected final void startNode(Attributes atts) throws SAXException {
145        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.NODE);
146    }
147
148    protected final void startWay(Attributes atts) throws SAXException {
149        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.WAY);
150    }
151
152    protected final void startRelation(Attributes atts) throws SAXException {
153        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.RELATION);
154    }
155
156    protected final void handleTag(Attributes atts) throws SAXException {
157        String key = getMandatoryAttributeString(atts, "k");
158        String value = getMandatoryAttributeString(atts, "v");
159        currentPrimitive.put(key, value);
160    }
161
162    protected final void handleNodeReference(Attributes atts) throws SAXException {
163        long ref = getMandatoryAttributeLong(atts, "ref");
164        ((HistoryWay) currentPrimitive).addNode(ref);
165    }
166
167    protected void handleMember(Attributes atts) throws SAXException {
168        long ref = getMandatoryAttributeLong(atts, "ref");
169        String v = getMandatoryAttributeString(atts, "type");
170        OsmPrimitiveType type = null;
171        try {
172            type = OsmPrimitiveType.fromApiTypeName(v);
173        } catch (IllegalArgumentException e) {
174            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type OsmPrimitiveType. Got ''{1}''.", "type", v), e);
175        }
176        String role = getMandatoryAttributeString(atts, "role");
177        RelationMemberData member = new RelationMemberData(role, type, ref);
178        ((HistoryRelation) currentPrimitive).addMember(member);
179    }
180
181    protected final boolean doStartElement(String qName, Attributes atts) throws SAXException {
182        switch (qName) {
183        case "node":
184            startNode(atts);
185            return true;
186        case "way":
187            startWay(atts);
188            return true;
189        case "relation":
190            startRelation(atts);
191            return true;
192        case "tag":
193            handleTag(atts);
194            return true;
195        case "nd":
196            handleNodeReference(atts);
197            return true;
198        case "member":
199            handleMember(atts);
200            return true;
201        default:
202            return false;
203        }
204    }
205}