source: josm/trunk/src/org/openstreetmap/josm/io/OsmChangesetParser.java@ 8512

Last change on this file since 8512 was 8510, checked in by Don-vip, 9 years ago

checkstyle: enable relevant whitespace checks and fix them

  • Property svn:eol-style set to native
File size: 10.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.InputStream;
7import java.io.InputStreamReader;
8import java.nio.charset.StandardCharsets;
9import java.text.MessageFormat;
10import java.util.Date;
11import java.util.LinkedList;
12import java.util.List;
13
14import javax.xml.parsers.ParserConfigurationException;
15
16import org.openstreetmap.josm.data.coor.LatLon;
17import org.openstreetmap.josm.data.osm.Changeset;
18import org.openstreetmap.josm.data.osm.ChangesetDiscussionComment;
19import org.openstreetmap.josm.data.osm.User;
20import org.openstreetmap.josm.gui.progress.ProgressMonitor;
21import org.openstreetmap.josm.tools.Utils;
22import org.openstreetmap.josm.tools.XmlParsingException;
23import org.openstreetmap.josm.tools.date.DateUtils;
24import org.xml.sax.Attributes;
25import org.xml.sax.InputSource;
26import org.xml.sax.Locator;
27import org.xml.sax.SAXException;
28import org.xml.sax.helpers.DefaultHandler;
29
30/**
31 * Parser for a list of changesets, encapsulated in an OSM data set structure.
32 * Example:
33 * <pre>
34 * &lt;osm version="0.6" generator="OpenStreetMap server"&gt;
35 * &lt;changeset id="143" user="guggis" uid="1" created_at="2009-09-08T20:35:39Z" closed_at="2009-09-08T21:36:12Z" open="false"
36 * min_lon="7.380925" min_lat="46.9215164" max_lon="7.3984718" max_lat="46.9226502"&gt;
37 * &lt;tag k="asdfasdf" v="asdfasdf"/&gt;
38 * &lt;tag k="created_by" v="JOSM/1.5 (UNKNOWN de)"/&gt;
39 * &lt;tag k="comment" v="1234"/&gt;
40 * &lt;/changeset&gt;
41 * &lt;/osm&gt;
42 * </pre>
43 *
44 */
45public final class OsmChangesetParser {
46 private final List<Changeset> changesets;
47
48 private OsmChangesetParser() {
49 changesets = new LinkedList<>();
50 }
51
52 /**
53 * Returns the parsed changesets.
54 * @return the parsed changesets
55 */
56 public List<Changeset> getChangesets() {
57 return changesets;
58 }
59
60 private class Parser extends DefaultHandler {
61 private Locator locator;
62
63 @Override
64 public void setDocumentLocator(Locator locator) {
65 this.locator = locator;
66 }
67
68 protected void throwException(String msg) throws XmlParsingException {
69 throw new XmlParsingException(msg).rememberLocation(locator);
70 }
71
72 /** The current changeset */
73 private Changeset current = null;
74
75 /** The current comment */
76 private ChangesetDiscussionComment comment = null;
77
78 /** The current comment text */
79 private StringBuilder text = null;
80
81 protected void parseChangesetAttributes(Changeset cs, Attributes atts) throws XmlParsingException {
82 // -- id
83 String value = atts.getValue("id");
84 if (value == null) {
85 throwException(tr("Missing mandatory attribute ''{0}''.", "id"));
86 }
87 current.setId(parseNumericAttribute(value, 1));
88
89 // -- user / uid
90 current.setUser(createUser(atts));
91
92 // -- created_at
93 value = atts.getValue("created_at");
94 if (value == null) {
95 current.setCreatedAt(null);
96 } else {
97 current.setCreatedAt(DateUtils.fromString(value));
98 }
99
100 // -- closed_at
101 value = atts.getValue("closed_at");
102 if (value == null) {
103 current.setClosedAt(null);
104 } else {
105 current.setClosedAt(DateUtils.fromString(value));
106 }
107
108 // -- open
109 value = atts.getValue("open");
110 if (value == null) {
111 throwException(tr("Missing mandatory attribute ''{0}''.", "open"));
112 } else if ("true".equals(value)) {
113 current.setOpen(true);
114 } else if ("false".equals(value)) {
115 current.setOpen(false);
116 } else {
117 throwException(tr("Illegal boolean value for attribute ''{0}''. Got ''{1}''.", "open", value));
118 }
119
120 // -- min_lon and min_lat
121 String min_lon = atts.getValue("min_lon");
122 String min_lat = atts.getValue("min_lat");
123 String max_lon = atts.getValue("max_lon");
124 String max_lat = atts.getValue("max_lat");
125 if (min_lon != null && min_lat != null && max_lon != null && max_lat != null) {
126 double minLon = 0;
127 try {
128 minLon = Double.parseDouble(min_lon);
129 } catch (NumberFormatException e) {
130 throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "min_lon", min_lon));
131 }
132 double minLat = 0;
133 try {
134 minLat = Double.parseDouble(min_lat);
135 } catch (NumberFormatException e) {
136 throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "min_lat", min_lat));
137 }
138 current.setMin(new LatLon(minLat, minLon));
139
140 // -- max_lon and max_lat
141
142 double maxLon = 0;
143 try {
144 maxLon = Double.parseDouble(max_lon);
145 } catch (NumberFormatException e) {
146 throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "max_lon", max_lon));
147 }
148 double maxLat = 0;
149 try {
150 maxLat = Double.parseDouble(max_lat);
151 } catch (NumberFormatException e) {
152 throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "max_lat", max_lat));
153 }
154 current.setMax(new LatLon(maxLon, maxLat));
155 }
156
157 // -- comments_count
158 String commentsCount = atts.getValue("comments_count");
159 if (commentsCount != null) {
160 current.setCommentsCount(parseNumericAttribute(commentsCount, 0));
161 }
162 }
163
164 private void parseCommentAttributes(Attributes atts) throws XmlParsingException {
165 // -- date
166 String value = atts.getValue("date");
167 Date date = null;
168 if (value != null) {
169 date = DateUtils.fromString(value);
170 }
171
172 comment = new ChangesetDiscussionComment(date, createUser(atts));
173 }
174
175 private int parseNumericAttribute(String value, int minAllowed) throws XmlParsingException {
176 int att = 0;
177 try {
178 att = Integer.parseInt(value);
179 } catch (NumberFormatException e) {
180 throwException(tr("Illegal value for attribute ''{0}''. Got ''{1}''.", "id", value));
181 }
182 if (att < minAllowed) {
183 throwException(tr("Illegal numeric value for attribute ''{0}''. Got ''{1}''.", "id", att));
184 }
185 return att;
186 }
187
188 @Override
189 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
190 switch (qName) {
191 case "osm":
192 if (atts == null) {
193 throwException(tr("Missing mandatory attribute ''{0}'' of XML element {1}.", "version", "osm"));
194 return;
195 }
196 String v = atts.getValue("version");
197 if (v == null) {
198 throwException(tr("Missing mandatory attribute ''{0}''.", "version"));
199 }
200 if (!("0.6".equals(v))) {
201 throwException(tr("Unsupported version: {0}", v));
202 }
203 break;
204 case "changeset":
205 current = new Changeset();
206 parseChangesetAttributes(current, atts);
207 break;
208 case "tag":
209 String key = atts.getValue("k");
210 String value = atts.getValue("v");
211 current.put(key, value);
212 break;
213 case "discussion":
214 break;
215 case "comment":
216 parseCommentAttributes(atts);
217 break;
218 case "text":
219 text = new StringBuilder();
220 break;
221 default:
222 throwException(tr("Undefined element ''{0}'' found in input stream. Aborting.", qName));
223 }
224 }
225
226 @Override
227 public void characters(char[] ch, int start, int length) throws SAXException {
228 if (text != null) {
229 text.append(ch, start, length);
230 }
231 }
232
233 @Override
234 public void endElement(String uri, String localName, String qName) throws SAXException {
235 if ("changeset".equals(qName)) {
236 changesets.add(current);
237 current = null;
238 } else if ("comment".equals(qName)) {
239 current.addDiscussionComment(comment);
240 comment = null;
241 } else if ("text".equals(qName)) {
242 comment.setText(text.toString());
243 text = null;
244 }
245 }
246
247 protected User createUser(Attributes atts) throws XmlParsingException {
248 String name = atts.getValue("user");
249 String uid = atts.getValue("uid");
250 if (uid == null) {
251 if (name == null)
252 return null;
253 return User.createLocalUser(name);
254 }
255 try {
256 long id = Long.parseLong(uid);
257 return User.createOsmUser(id, name);
258 } catch (NumberFormatException e) {
259 throwException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid));
260 }
261 return null;
262 }
263 }
264
265 /**
266 * Parse the given input source and return the list of changesets
267 *
268 * @param source the source input stream
269 * @param progressMonitor the progress monitor
270 *
271 * @return the list of changesets
272 * @throws IllegalDataException if the an error was found while parsing the data from the source
273 */
274 @SuppressWarnings("resource")
275 public static List<Changeset> parse(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
276 OsmChangesetParser parser = new OsmChangesetParser();
277 try {
278 progressMonitor.beginTask("");
279 progressMonitor.indeterminateSubTask(tr("Parsing list of changesets..."));
280 InputSource inputSource = new InputSource(new InvalidXmlCharacterFilter(new InputStreamReader(source, StandardCharsets.UTF_8)));
281 Utils.parseSafeSAX(inputSource, parser.new Parser());
282 return parser.getChangesets();
283 } catch (ParserConfigurationException | SAXException e) {
284 throw new IllegalDataException(e.getMessage(), e);
285 } catch (Exception e) {
286 throw new IllegalDataException(e);
287 } finally {
288 progressMonitor.finishTask();
289 }
290 }
291}
Note: See TracBrowser for help on using the repository browser.