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