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.io.InputStream;
007import java.util.Arrays;
008
009import javax.xml.stream.XMLStreamConstants;
010import javax.xml.stream.XMLStreamException;
011
012import org.openstreetmap.josm.data.osm.DataSet;
013import org.openstreetmap.josm.data.osm.OsmPrimitive;
014import org.openstreetmap.josm.gui.progress.ProgressMonitor;
015
016/**
017 * Reader for <a href="http://wiki.openstreetmap.org/wiki/OsmChange">OsmChange</a> file format.
018 */
019public class OsmChangeReader extends OsmReader {
020
021    /**
022     * List of possible actions.
023     */
024    private static final String[] ACTIONS = {"create", "modify", "delete"};
025
026    /**
027     * constructor (for private and subclasses use only)
028     *
029     * @see #parseDataSet(InputStream, ProgressMonitor)
030     */
031    protected OsmChangeReader() {
032    }
033
034    @Override
035    protected void parseRoot() throws XMLStreamException {
036        if ("osmChange".equals(parser.getLocalName())) {
037            parseOsmChange();
038        } else {
039            parseUnknown();
040        }
041    }
042
043    private void parseOsmChange() throws XMLStreamException {
044        String v = parser.getAttributeValue(null, "version");
045        if (v == null) {
046            throwException(tr("Missing mandatory attribute ''{0}''.", "version"));
047        }
048        if (!"0.6".equals(v)) {
049            throwException(tr("Unsupported version: {0}", v));
050        }
051        ds.setVersion(v);
052        while (parser.hasNext()) {
053            int event = parser.next();
054            if (event == XMLStreamConstants.START_ELEMENT) {
055                if (Arrays.asList(ACTIONS).contains(parser.getLocalName())) {
056                    parseCommon(parser.getLocalName());
057                } else {
058                    parseUnknown();
059                }
060            } else if (event == XMLStreamConstants.END_ELEMENT) {
061                return;
062            }
063        }
064    }
065
066    private void parseCommon(String action) throws XMLStreamException {
067        while (parser.hasNext()) {
068            int event = parser.next();
069            if (event == XMLStreamConstants.START_ELEMENT) {
070                OsmPrimitive p = null;
071                switch (parser.getLocalName()) {
072                case "node":
073                    p = parseNode();
074                    break;
075                case "way":
076                    p = parseWay();
077                    break;
078                case "relation":
079                    p = parseRelation();
080                    break;
081                default:
082                    parseUnknown();
083                }
084                if (p != null && action != null) {
085                    if ("modify".equals(action)) {
086                        p.setModified(true);
087                    } else if ("delete".equals(action)) {
088                        p.setDeleted(true);
089                    }
090                }
091            } else if (event == XMLStreamConstants.END_ELEMENT) {
092                return;
093            }
094        }
095    }
096
097    /**
098     * Parse the given input source and return the dataset.
099     *
100     * @param source the source input stream. Must not be <code>null</code>.
101     * @param progressMonitor  the progress monitor. If <code>null</code>,
102     * {@link org.openstreetmap.josm.gui.progress.NullProgressMonitor#INSTANCE} is assumed
103     *
104     * @return the dataset with the parsed data
105     * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
106     * @throws IllegalArgumentException thrown if source is <code>null</code>
107     */
108    public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
109        return new OsmChangeReader().doParseDataSet(source, progressMonitor);
110    }
111}