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            } else {
120                user = User.createLocalUser(userStr);
121            }
122        } else {
123            user = User.getAnonymous();
124        }
125
126        String v = getMandatoryAttributeString(atts, "timestamp");
127        Date timestamp = DateUtils.fromString(v);
128        HistoryOsmPrimitive primitive = null;
129        if (type.equals(OsmPrimitiveType.NODE)) {
130            Double lat = getAttributeDouble(atts, "lat");
131            Double lon = getAttributeDouble(atts, "lon");
132            LatLon coor = (lat != null && lon != null) ? new LatLon(lat, lon) : null;
133            primitive = new HistoryNode(id, version, visible, user, changesetId, timestamp, coor);
134
135        } else if (type.equals(OsmPrimitiveType.WAY)) {
136            primitive = new HistoryWay(id, version, visible, user, changesetId, timestamp);
137        } else if (type.equals(OsmPrimitiveType.RELATION)) {
138            primitive = new HistoryRelation(id, version, visible, user, changesetId, timestamp);
139        }
140        return primitive;
141    }
142
143    protected final void startNode(Attributes atts) throws SAXException {
144        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.NODE);
145    }
146
147    protected final void startWay(Attributes atts) throws SAXException {
148        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.WAY);
149    }
150
151    protected final void startRelation(Attributes atts) throws SAXException {
152        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.RELATION);
153    }
154
155    protected final void handleTag(Attributes atts) throws SAXException {
156        String key = getMandatoryAttributeString(atts, "k");
157        String value = getMandatoryAttributeString(atts, "v");
158        currentPrimitive.put(key, value);
159    }
160
161    protected final void handleNodeReference(Attributes atts) throws SAXException {
162        long ref = getMandatoryAttributeLong(atts, "ref");
163        ((HistoryWay) currentPrimitive).addNode(ref);
164    }
165
166    protected void handleMember(Attributes atts) throws SAXException {
167        long ref = getMandatoryAttributeLong(atts, "ref");
168        String v = getMandatoryAttributeString(atts, "type");
169        OsmPrimitiveType type = null;
170        try {
171            type = OsmPrimitiveType.fromApiTypeName(v);
172        } catch (IllegalArgumentException e) {
173            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type OsmPrimitiveType. Got ''{1}''.", "type", v), e);
174        }
175        String role = getMandatoryAttributeString(atts, "role");
176        RelationMemberData member = new RelationMemberData(role, type, ref);
177        ((HistoryRelation) currentPrimitive).addMember(member);
178    }
179
180    protected final boolean doStartElement(String qName, Attributes atts) throws SAXException {
181        switch (qName) {
182        case "node":
183            startNode(atts);
184            return true;
185        case "way":
186            startWay(atts);
187            return true;
188        case "relation":
189            startRelation(atts);
190            return true;
191        case "tag":
192            handleTag(atts);
193            return true;
194        case "nd":
195            handleNodeReference(atts);
196            return true;
197        case "member":
198            handleMember(atts);
199            return true;
200        default:
201            return false;
202        }
203    }
204}