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