[3719] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[2512] | 2 | package org.openstreetmap.josm.io;
|
---|
| 3 |
|
---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
| 6 | import java.io.InputStream;
|
---|
| 7 | import java.io.InputStreamReader;
|
---|
[2852] | 8 | import java.text.MessageFormat;
|
---|
[2512] | 9 | import java.util.LinkedList;
|
---|
| 10 | import java.util.List;
|
---|
| 11 |
|
---|
| 12 | import javax.xml.parsers.ParserConfigurationException;
|
---|
| 13 | import javax.xml.parsers.SAXParserFactory;
|
---|
| 14 |
|
---|
| 15 | import org.openstreetmap.josm.data.coor.LatLon;
|
---|
| 16 | import org.openstreetmap.josm.data.osm.Changeset;
|
---|
| 17 | import org.openstreetmap.josm.data.osm.User;
|
---|
| 18 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
| 19 | import org.openstreetmap.josm.tools.DateUtils;
|
---|
[6552] | 20 | import org.openstreetmap.josm.tools.Utils;
|
---|
[6906] | 21 | import org.openstreetmap.josm.tools.XmlParsingException;
|
---|
[2512] | 22 | import org.xml.sax.Attributes;
|
---|
| 23 | import org.xml.sax.InputSource;
|
---|
| 24 | import org.xml.sax.Locator;
|
---|
| 25 | import org.xml.sax.SAXException;
|
---|
| 26 | import org.xml.sax.helpers.DefaultHandler;
|
---|
| 27 |
|
---|
| 28 | /**
|
---|
| 29 | * Parser for a list of changesets, encapsulated in an OSM data set structure.
|
---|
| 30 | * Example:
|
---|
| 31 | * <pre>
|
---|
| 32 | * <osm version="0.6" generator="OpenStreetMap server">
|
---|
| 33 | * <changeset id="143" user="guggis" uid="1" created_at="2009-09-08T20:35:39Z" closed_at="2009-09-08T21:36:12Z" open="false" min_lon="7.380925" min_lat="46.9215164" max_lon="7.3984718" max_lat="46.9226502">
|
---|
| 34 | * <tag k="asdfasdf" v="asdfasdf"/>
|
---|
| 35 | * <tag k="created_by" v="JOSM/1.5 (UNKNOWN de)"/>
|
---|
| 36 | * <tag k="comment" v="1234"/>
|
---|
| 37 | * </changeset>
|
---|
| 38 | * </osm>
|
---|
| 39 | * </pre>
|
---|
| 40 | *
|
---|
| 41 | */
|
---|
[6362] | 42 | public final class OsmChangesetParser {
|
---|
[2512] | 43 | private List<Changeset> changesets;
|
---|
| 44 |
|
---|
| 45 | private OsmChangesetParser() {
|
---|
| 46 | changesets = new LinkedList<Changeset>();
|
---|
| 47 | }
|
---|
| 48 |
|
---|
[6906] | 49 | /**
|
---|
| 50 | * Returns the parsed changesets.
|
---|
| 51 | * @return the parsed changesets
|
---|
| 52 | */
|
---|
[2512] | 53 | public List<Changeset> getChangesets() {
|
---|
| 54 | return changesets;
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | private class Parser extends DefaultHandler {
|
---|
| 58 | private Locator locator;
|
---|
| 59 |
|
---|
| 60 | @Override
|
---|
| 61 | public void setDocumentLocator(Locator locator) {
|
---|
| 62 | this.locator = locator;
|
---|
| 63 | }
|
---|
| 64 |
|
---|
[6906] | 65 | protected void throwException(String msg) throws XmlParsingException {
|
---|
| 66 | throw new XmlParsingException(msg).rememberLocation(locator);
|
---|
[2512] | 67 | }
|
---|
[6906] | 68 |
|
---|
| 69 | /** The current changeset */
|
---|
[2512] | 70 | private Changeset current = null;
|
---|
| 71 |
|
---|
[6906] | 72 | protected void parseChangesetAttributes(Changeset cs, Attributes atts) throws XmlParsingException {
|
---|
[2512] | 73 | // -- id
|
---|
| 74 | String value = atts.getValue("id");
|
---|
| 75 | if (value == null) {
|
---|
| 76 | throwException(tr("Missing mandatory attribute ''{0}''.", "id"));
|
---|
| 77 | }
|
---|
[2604] | 78 | int id = 0;
|
---|
[2512] | 79 | try {
|
---|
[2604] | 80 | id = Integer.parseInt(value);
|
---|
[2512] | 81 | } catch(NumberFormatException e) {
|
---|
| 82 | throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "id", value));
|
---|
| 83 | }
|
---|
| 84 | if (id <= 0) {
|
---|
[2955] | 85 | throwException(tr("Illegal numeric value for attribute ''{0}''. Got ''{1}''.", "id", id));
|
---|
[2512] | 86 | }
|
---|
| 87 | current.setId(id);
|
---|
| 88 |
|
---|
| 89 | // -- user
|
---|
| 90 | String user = atts.getValue("user");
|
---|
| 91 | String uid = atts.getValue("uid");
|
---|
| 92 | current.setUser(createUser(uid, user));
|
---|
| 93 |
|
---|
| 94 | // -- created_at
|
---|
| 95 | value = atts.getValue("created_at");
|
---|
| 96 | if (value == null) {
|
---|
| 97 | current.setCreatedAt(null);
|
---|
| 98 | } else {
|
---|
| 99 | current.setCreatedAt(DateUtils.fromString(value));
|
---|
| 100 | }
|
---|
| 101 |
|
---|
| 102 | // -- closed_at
|
---|
| 103 | value = atts.getValue("closed_at");
|
---|
| 104 | if (value == null) {
|
---|
| 105 | current.setClosedAt(null);
|
---|
| 106 | } else {
|
---|
| 107 | current.setClosedAt(DateUtils.fromString(value));
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | // -- open
|
---|
| 111 | value = atts.getValue("open");
|
---|
| 112 | if (value == null) {
|
---|
| 113 | throwException(tr("Missing mandatory attribute ''{0}''.", "open"));
|
---|
| 114 | } else if (value.equals("true")) {
|
---|
| 115 | current.setOpen(true);
|
---|
| 116 | } else if (value.equals("false")) {
|
---|
| 117 | current.setOpen(false);
|
---|
| 118 | } else {
|
---|
| 119 | throwException(tr("Illegal boolean value for attribute ''{0}''. Got ''{1}''.", "open", value));
|
---|
| 120 | }
|
---|
| 121 |
|
---|
| 122 | // -- min_lon and min_lat
|
---|
| 123 | String min_lon = atts.getValue("min_lon");
|
---|
| 124 | String min_lat = atts.getValue("min_lat");
|
---|
| 125 | String max_lon = atts.getValue("max_lon");
|
---|
| 126 | String max_lat = atts.getValue("max_lat");
|
---|
| 127 | if (min_lon != null && min_lat != null && max_lon != null && max_lat != null) {
|
---|
| 128 | double minLon = 0;
|
---|
| 129 | try {
|
---|
| 130 | minLon = Double.parseDouble(min_lon);
|
---|
| 131 | } catch(NumberFormatException e) {
|
---|
| 132 | throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "min_lon", min_lon));
|
---|
| 133 | }
|
---|
| 134 | double minLat = 0;
|
---|
| 135 | try {
|
---|
| 136 | minLat = Double.parseDouble(min_lat);
|
---|
| 137 | } catch(NumberFormatException e) {
|
---|
| 138 | throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "min_lat", min_lat));
|
---|
| 139 | }
|
---|
| 140 | current.setMin(new LatLon(minLat, minLon));
|
---|
| 141 |
|
---|
| 142 | // -- max_lon and max_lat
|
---|
| 143 |
|
---|
| 144 | double maxLon = 0;
|
---|
| 145 | try {
|
---|
| 146 | maxLon = Double.parseDouble(max_lon);
|
---|
| 147 | } catch(NumberFormatException e) {
|
---|
| 148 | throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "max_lon", max_lon));
|
---|
| 149 | }
|
---|
| 150 | double maxLat = 0;
|
---|
| 151 | try {
|
---|
| 152 | maxLat = Double.parseDouble(max_lat);
|
---|
| 153 | } catch(NumberFormatException e) {
|
---|
| 154 | throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "max_lat", max_lat));
|
---|
| 155 | }
|
---|
| 156 | current.setMax(new LatLon(maxLon, maxLat));
|
---|
| 157 | }
|
---|
| 158 | }
|
---|
| 159 |
|
---|
[6787] | 160 | @Override
|
---|
| 161 | public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
|
---|
[2512] | 162 | if (qName.equals("osm")) {
|
---|
| 163 | if (atts == null) {
|
---|
| 164 | throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "version", "osm"));
|
---|
| 165 | }
|
---|
| 166 | String v = atts.getValue("version");
|
---|
| 167 | if (v == null) {
|
---|
| 168 | throwException(tr("Missing mandatory attribute ''{0}''.", "version"));
|
---|
| 169 | }
|
---|
| 170 | if (!(v.equals("0.6"))) {
|
---|
| 171 | throwException(tr("Unsupported version: {0}", v));
|
---|
| 172 | }
|
---|
| 173 | } else if (qName.equals("changeset")) {
|
---|
| 174 | current = new Changeset();
|
---|
| 175 | parseChangesetAttributes(current, atts);
|
---|
| 176 | } else if (qName.equals("tag")) {
|
---|
| 177 | String key = atts.getValue("k");
|
---|
| 178 | String value = atts.getValue("v");
|
---|
| 179 | current.put(key, value);
|
---|
| 180 | } else {
|
---|
| 181 | throwException(tr("Undefined element ''{0}'' found in input stream. Aborting.", qName));
|
---|
| 182 | }
|
---|
| 183 | }
|
---|
| 184 |
|
---|
| 185 | @Override
|
---|
| 186 | public void endElement(String uri, String localName, String qName) throws SAXException {
|
---|
| 187 | if (qName.equals("changeset")) {
|
---|
| 188 | changesets.add(current);
|
---|
| 189 | }
|
---|
| 190 | }
|
---|
| 191 |
|
---|
[6906] | 192 | protected User createUser(String uid, String name) throws XmlParsingException {
|
---|
[2512] | 193 | if (uid == null) {
|
---|
| 194 | if (name == null)
|
---|
| 195 | return null;
|
---|
| 196 | return User.createLocalUser(name);
|
---|
| 197 | }
|
---|
| 198 | try {
|
---|
| 199 | long id = Long.parseLong(uid);
|
---|
| 200 | return User.createOsmUser(id, name);
|
---|
| 201 | } catch(NumberFormatException e) {
|
---|
[2852] | 202 | throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid));
|
---|
[2512] | 203 | }
|
---|
| 204 | return null;
|
---|
| 205 | }
|
---|
| 206 | }
|
---|
| 207 |
|
---|
| 208 | /**
|
---|
| 209 | * Parse the given input source and return the list of changesets
|
---|
| 210 | *
|
---|
| 211 | * @param source the source input stream
|
---|
| 212 | * @param progressMonitor the progress monitor
|
---|
| 213 | *
|
---|
| 214 | * @return the list of changesets
|
---|
| 215 | * @throws IllegalDataException thrown if the an error was found while parsing the data from the source
|
---|
| 216 | */
|
---|
| 217 | public static List<Changeset> parse(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
|
---|
| 218 | OsmChangesetParser parser = new OsmChangesetParser();
|
---|
| 219 | try {
|
---|
[2828] | 220 | progressMonitor.beginTask("");
|
---|
[2688] | 221 | progressMonitor.indeterminateSubTask(tr("Parsing list of changesets..."));
|
---|
[6787] | 222 | InputSource inputSource = new InputSource(new InvalidXmlCharacterFilter(new InputStreamReader(source, Utils.UTF_8)));
|
---|
[2512] | 223 | SAXParserFactory.newInstance().newSAXParser().parse(inputSource, parser.new Parser());
|
---|
| 224 | return parser.getChangesets();
|
---|
| 225 | } catch(ParserConfigurationException e) {
|
---|
| 226 | throw new IllegalDataException(e.getMessage(), e);
|
---|
| 227 | } catch(SAXException e) {
|
---|
| 228 | throw new IllegalDataException(e.getMessage(), e);
|
---|
| 229 | } catch(Exception e) {
|
---|
| 230 | throw new IllegalDataException(e);
|
---|
| 231 | } finally {
|
---|
| 232 | progressMonitor.finishTask();
|
---|
| 233 | }
|
---|
| 234 | }
|
---|
| 235 | }
|
---|