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.time.Instant;
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    /** if true, replace user information in input by anonymous user */
032    protected boolean useAnonymousUser;
033
034    @Override
035    public void setDocumentLocator(Locator locator) {
036        this.locator = locator;
037    }
038
039    protected abstract void throwException(String message) throws SAXException;
040
041    protected abstract void throwException(String message, Exception e) throws SAXException;
042
043    protected final long getMandatoryAttributeLong(Attributes attr, String name) throws SAXException {
044        String v = attr.getValue(name);
045        if (v == null) {
046            throwException(tr("Missing mandatory attribute ''{0}''.", name));
047        }
048        long l = 0L;
049        try {
050            l = Long.parseLong(v);
051        } catch (NumberFormatException e) {
052            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long. Got ''{1}''.", name, v), e);
053        }
054        if (l < 0) {
055            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long (>=0). Got ''{1}''.", name, v));
056        }
057        return l;
058    }
059
060    protected final Long getAttributeLong(Attributes attr, String name) throws SAXException {
061        String v = attr.getValue(name);
062        if (v == null)
063            return null;
064        Long l = 0L;
065        try {
066            l = Long.valueOf(v);
067        } catch (NumberFormatException e) {
068            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long. Got ''{1}''.", name, v), e);
069        }
070        if (l < 0) {
071            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long (>=0). Got ''{1}''.", name, v));
072        }
073        return l;
074    }
075
076    protected final Double getAttributeDouble(Attributes attr, String name) throws SAXException {
077        String v = attr.getValue(name);
078        if (v == null) {
079            return null;
080        }
081        double d = 0.0;
082        try {
083            d = Double.parseDouble(v);
084        } catch (NumberFormatException e) {
085            throwException(tr("Illegal value for attribute ''{0}'' of type double. Got ''{1}''.", name, v), e);
086        }
087        return d;
088    }
089
090    protected final String getMandatoryAttributeString(Attributes attr, String name) throws SAXException {
091        String v = attr.getValue(name);
092        if (v == null) {
093            throwException(tr("Missing mandatory attribute ''{0}''.", name));
094        }
095        return v;
096    }
097
098    protected boolean getMandatoryAttributeBoolean(Attributes attr, String name) throws SAXException {
099        String v = attr.getValue(name);
100        if (v == null) {
101            throwException(tr("Missing mandatory attribute ''{0}''.", name));
102        }
103        if ("true".equals(v)) return true;
104        if ("false".equals(v)) return false;
105        throwException(tr("Illegal value for mandatory attribute ''{0}'' of type boolean. Got ''{1}''.", name, v));
106        return false; // not reached
107    }
108
109    protected final HistoryOsmPrimitive createPrimitive(Attributes atts, OsmPrimitiveType type) throws SAXException {
110        long id = getMandatoryAttributeLong(atts, "id");
111        long version = getMandatoryAttributeLong(atts, "version");
112        Long changeset = getAttributeLong(atts, "changeset");
113        long changesetId = changeset != null ? changeset : 0L;
114        boolean visible = getMandatoryAttributeBoolean(atts, "visible");
115
116        User user = null;
117        if (!useAnonymousUser) {
118            Long uid = getAttributeLong(atts, "uid");
119            String userStr = atts.getValue("user");
120            if (userStr != null) {
121                if (uid != null) {
122                    user = User.createOsmUser(uid, userStr);
123                    user.setPreferredName(userStr);
124                } else {
125                    user = User.createLocalUser(userStr);
126                }
127            }
128        }
129        if (user == null) {
130            user = User.getAnonymous();
131        }
132
133        String v = getMandatoryAttributeString(atts, "timestamp");
134        Instant timestamp = DateUtils.parseInstant(v);
135        HistoryOsmPrimitive primitive = null;
136        if (type == OsmPrimitiveType.NODE) {
137            Double lat = getAttributeDouble(atts, "lat");
138            Double lon = getAttributeDouble(atts, "lon");
139            LatLon coor = (lat != null && lon != null) ? new LatLon(lat, lon) : null;
140            primitive = new HistoryNode(id, version, visible, user, changesetId, timestamp, coor, changeset != null);
141
142        } else if (type == OsmPrimitiveType.WAY) {
143            primitive = new HistoryWay(id, version, visible, user, changesetId, timestamp, changeset != null);
144        } else if (type == OsmPrimitiveType.RELATION) {
145            primitive = new HistoryRelation(id, version, visible, user, changesetId, timestamp, changeset != null);
146        }
147        return primitive;
148    }
149
150    protected final void startNode(Attributes atts) throws SAXException {
151        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.NODE);
152    }
153
154    protected final void startWay(Attributes atts) throws SAXException {
155        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.WAY);
156    }
157
158    protected final void startRelation(Attributes atts) throws SAXException {
159        currentPrimitive = createPrimitive(atts, OsmPrimitiveType.RELATION);
160    }
161
162    protected final void handleTag(Attributes atts) throws SAXException {
163        String key = getMandatoryAttributeString(atts, "k");
164        String value = getMandatoryAttributeString(atts, "v");
165        currentPrimitive.put(key, value);
166    }
167
168    protected final void handleNodeReference(Attributes atts) throws SAXException {
169        long ref = getMandatoryAttributeLong(atts, "ref");
170        ((HistoryWay) currentPrimitive).addNode(ref);
171    }
172
173    protected void handleMember(Attributes atts) throws SAXException {
174        long ref = getMandatoryAttributeLong(atts, "ref");
175        String v = getMandatoryAttributeString(atts, "type");
176        OsmPrimitiveType type = null;
177        try {
178            type = OsmPrimitiveType.fromApiTypeName(v);
179        } catch (IllegalArgumentException e) {
180            throwException(tr("Illegal value for mandatory attribute ''{0}'' of type OsmPrimitiveType. Got ''{1}''.", "type", v), e);
181        }
182        String role = getMandatoryAttributeString(atts, "role");
183        RelationMemberData member = new RelationMemberData(role, type, ref);
184        ((HistoryRelation) currentPrimitive).addMember(member);
185    }
186
187    protected final boolean doStartElement(String qName, Attributes atts) throws SAXException {
188        switch (qName) {
189        case "node":
190            startNode(atts);
191            return true;
192        case "way":
193            startWay(atts);
194            return true;
195        case "relation":
196            startRelation(atts);
197            return true;
198        case "tag":
199            handleTag(atts);
200            return true;
201        case "nd":
202            handleNodeReference(atts);
203            return true;
204        case "member":
205            handleMember(atts);
206            return true;
207        default:
208            return false;
209        }
210    }
211}