source: josm/trunk/src/org/openstreetmap/josm/io/OsmChangesetContentParser.java@ 3321

Last change on this file since 3321 was 3083, checked in by bastiK, 14 years ago

added svn:eol-style=native to source files

  • Property svn:eol-style set to native
File size: 13.7 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.io.InputStream;
8import java.io.InputStreamReader;
9import java.io.StringReader;
10import java.io.UnsupportedEncodingException;
11import java.util.Date;
12
13import javax.xml.parsers.ParserConfigurationException;
14import javax.xml.parsers.SAXParserFactory;
15
16import org.openstreetmap.josm.data.coor.LatLon;
17import org.openstreetmap.josm.data.osm.ChangesetDataSet;
18import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
19import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType;
20import org.openstreetmap.josm.data.osm.history.HistoryNode;
21import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
22import org.openstreetmap.josm.data.osm.history.HistoryRelation;
23import org.openstreetmap.josm.data.osm.history.HistoryWay;
24import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
25import org.openstreetmap.josm.gui.progress.ProgressMonitor;
26import org.openstreetmap.josm.tools.CheckParameterUtil;
27import org.openstreetmap.josm.tools.DateUtils;
28import org.xml.sax.Attributes;
29import org.xml.sax.InputSource;
30import org.xml.sax.Locator;
31import org.xml.sax.SAXException;
32import org.xml.sax.SAXParseException;
33import org.xml.sax.helpers.DefaultHandler;
34
35/**
36 * Parser for OSM changeset content.
37 *
38 */
39public class OsmChangesetContentParser {
40
41 private InputSource source;
42 private ChangesetDataSet data;
43
44 private class Parser extends DefaultHandler {
45
46 /** the current primitive to be read */
47 private HistoryOsmPrimitive currentPrimitive;
48 /** the current change modification type */
49 private ChangesetDataSet.ChangesetModificationType currentModificationType;
50
51 private Locator locator;
52
53 @Override
54 public void setDocumentLocator(Locator locator) {
55 this.locator = locator;
56 }
57
58 protected void throwException(String message) throws OsmDataParsingException {
59 throw new OsmDataParsingException(
60 message
61 ).rememberLocation(locator);
62 }
63
64 protected void throwException(Exception e) throws OsmDataParsingException {
65 throw new OsmDataParsingException(
66 e
67 ).rememberLocation(locator);
68 }
69
70 protected long getMandatoryAttributeLong(Attributes attr, String name) throws SAXException{
71 String v = attr.getValue(name);
72 if (v == null) {
73 throwException(tr("Missing mandatory attribute ''{0}''.", name));
74 }
75 Long l = 0l;
76 try {
77 l = Long.parseLong(v);
78 } catch(NumberFormatException e) {
79 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long. Got ''{1}''.", name, v));
80 }
81 if (l < 0) {
82 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long (>=0). Got ''{1}''.", name, v));
83 }
84 return l;
85 }
86
87 protected long getAttributeLong(Attributes attr, String name, long defaultValue) throws SAXException{
88 String v = attr.getValue(name);
89 if (v == null)
90 return defaultValue;
91 Long l = 0l;
92 try {
93 l = Long.parseLong(v);
94 } catch(NumberFormatException e) {
95 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long. Got ''{1}''.", name, v));
96 }
97 if (l < 0) {
98 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type long (>=0). Got ''{1}''.", name, v));
99 }
100 return l;
101 }
102
103 protected Double getMandatoryAttributeDouble(Attributes attr, String name) throws SAXException{
104 String v = attr.getValue(name);
105 if (v == null) {
106 throwException(tr("Missing mandatory attribute ''{0}''.", name));
107 }
108 double d = 0.0;
109 try {
110 d = Double.parseDouble(v);
111 } catch(NumberFormatException e) {
112 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type double. Got ''{1}''.", name, v));
113 }
114 return d;
115 }
116
117 protected String getMandatoryAttributeString(Attributes attr, String name) throws SAXException{
118 String v = attr.getValue(name);
119 if (v == null) {
120 throwException(tr("Missing mandatory attribute ''{0}''.", name));
121 }
122 return v;
123 }
124
125 protected String getAttributeString(Attributes attr, String name, String defaultValue) {
126 String v = attr.getValue(name);
127 if (v == null)
128 return defaultValue;
129 return v;
130 }
131
132 protected boolean getMandatoryAttributeBoolean(Attributes attr, String name) throws SAXException{
133 String v = attr.getValue(name);
134 if (v == null) {
135 throwException(tr("Missing mandatory attribute ''{0}''.", name));
136 }
137 if ("true".equals(v)) return true;
138 if ("false".equals(v)) return false;
139 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type boolean. Got ''{1}''.", name, v));
140 // not reached
141 return false;
142 }
143
144 protected HistoryOsmPrimitive createPrimitive(Attributes atts, OsmPrimitiveType type) throws SAXException {
145 long id = getMandatoryAttributeLong(atts,"id");
146 long version = getMandatoryAttributeLong(atts,"version");
147 long changesetId = getMandatoryAttributeLong(atts,"changeset");
148 boolean visible= getMandatoryAttributeBoolean(atts, "visible");
149 long uid = getAttributeLong(atts, "uid",-1);
150 String user = getAttributeString(atts, "user", tr("<anonymous>"));
151 String v = getMandatoryAttributeString(atts, "timestamp");
152 Date timestamp = DateUtils.fromString(v);
153 HistoryOsmPrimitive primitive = null;
154 if (type.equals(OsmPrimitiveType.NODE)) {
155 double lat = getMandatoryAttributeDouble(atts, "lat");
156 double lon = getMandatoryAttributeDouble(atts, "lon");
157 primitive = new HistoryNode(
158 id,version,visible,user,uid,changesetId,timestamp, new LatLon(lat,lon)
159 );
160
161 } else if (type.equals(OsmPrimitiveType.WAY)) {
162 primitive = new HistoryWay(
163 id,version,visible,user,uid,changesetId,timestamp
164 );
165 }if (type.equals(OsmPrimitiveType.RELATION)) {
166 primitive = new HistoryRelation(
167 id,version,visible,user,uid,changesetId,timestamp
168 );
169 }
170 return primitive;
171 }
172
173 protected void startNode(Attributes atts) throws SAXException {
174 currentPrimitive= createPrimitive(atts, OsmPrimitiveType.NODE);
175 }
176
177 protected void startWay(Attributes atts) throws SAXException {
178 currentPrimitive= createPrimitive(atts, OsmPrimitiveType.WAY);
179 }
180 protected void startRelation(Attributes atts) throws SAXException {
181 currentPrimitive= createPrimitive(atts, OsmPrimitiveType.RELATION);
182 }
183
184 protected void handleTag(Attributes atts) throws SAXException {
185 String key= getMandatoryAttributeString(atts, "k");
186 String value= getMandatoryAttributeString(atts, "v");
187 currentPrimitive.put(key,value);
188 }
189
190 protected void handleNodeReference(Attributes atts) throws SAXException {
191 long ref = getMandatoryAttributeLong(atts, "ref");
192 ((HistoryWay)currentPrimitive).addNode(ref);
193 }
194
195 protected void handleMember(Attributes atts) throws SAXException {
196 long ref = getMandatoryAttributeLong(atts, "ref");
197 String v = getMandatoryAttributeString(atts, "type");
198 OsmPrimitiveType type = null;
199 try {
200 type = OsmPrimitiveType.fromApiTypeName(v);
201 } catch(IllegalArgumentException e) {
202 throwException(tr("Illegal value for mandatory attribute ''{0}'' of type OsmPrimitiveType. Got ''{1}''.", "type", v));
203 }
204 String role = getMandatoryAttributeString(atts, "role");
205 org.openstreetmap.josm.data.osm.history.RelationMember member = new org.openstreetmap.josm.data.osm.history.RelationMember(role, type,ref);
206 ((HistoryRelation)currentPrimitive).addMember(member);
207 }
208
209 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
210 if (qName.equals("node")) {
211 startNode(atts);
212 } else if (qName.equals("way")) {
213 startWay(atts);
214 } else if (qName.equals("relation")) {
215 startRelation(atts);
216 } else if (qName.equals("tag")) {
217 handleTag(atts);
218 } else if (qName.equals("nd")) {
219 handleNodeReference(atts);
220 } else if (qName.equals("member")) {
221 handleMember(atts);
222 } else if (qName.equals("osmChange")) {
223 // do nothing
224 } else if (qName.equals("create")) {
225 currentModificationType = ChangesetModificationType.CREATED;
226 } else if (qName.equals("modify")) {
227 currentModificationType = ChangesetModificationType.UPDATED;
228 } else if (qName.equals("delete")) {
229 currentModificationType = ChangesetModificationType.DELETED;
230 } else {
231 System.err.println(tr("Warning: unsupported start element ''{0}'' in changeset content at position ({1},{2}). Skipping.", qName, locator.getLineNumber(), locator.getColumnNumber()));
232 }
233 }
234
235 @Override
236 public void endElement(String uri, String localName, String qName) throws SAXException {
237 if (qName.equals("node")
238 || qName.equals("way")
239 || qName.equals("relation")) {
240 if (currentModificationType == null) {
241 throwException(tr("Illegal document structure. Found node, way, or relation outside of ''create'', ''modify'', or ''delete''."));
242 }
243 data.put(currentPrimitive, currentModificationType);
244 } else if (qName.equals("osmChange")) {
245 // do nothing
246 } else if (qName.equals("create")) {
247 currentModificationType = null;
248 } else if (qName.equals("modify")) {
249 currentModificationType = null;
250 } else if (qName.equals("delete")) {
251 currentModificationType = null;
252 } else if (qName.equals("tag")) {
253 // do nothing
254 } else if (qName.equals("nd")) {
255 // do nothing
256 } else if (qName.equals("member")) {
257 // do nothing
258 } else {
259 System.err.println(tr("Warning: unsupported end element ''{0}'' in changeset content at position ({1},{2}). Skipping.", qName, locator.getLineNumber(), locator.getColumnNumber()));
260 }
261 }
262
263 @Override
264 public void error(SAXParseException e) throws SAXException {
265 throwException(e);
266 }
267
268 @Override
269 public void fatalError(SAXParseException e) throws SAXException {
270 throwException(e);
271 }
272 }
273
274 /**
275 * Create a parser
276 *
277 * @param source the input stream with the changeset content as XML document. Must not be null.
278 * @throws IllegalArgumentException thrown if source is null.
279 */
280 public OsmChangesetContentParser(InputStream source) throws UnsupportedEncodingException {
281 CheckParameterUtil.ensureParameterNotNull(source, "source");
282 this.source = new InputSource(new InputStreamReader(source, "UTF-8"));
283 data = new ChangesetDataSet();
284 }
285
286 public OsmChangesetContentParser(String source) {
287 CheckParameterUtil.ensureParameterNotNull(source, "source");
288 this.source = new InputSource(new StringReader(source));
289 data = new ChangesetDataSet();
290 }
291
292 /**
293 * Parses the content
294 *
295 * @param progressMonitor the progress monitor. Set to {@see NullProgressMonitor#INSTANCE}
296 * if null
297 * @return the parsed data
298 * @throws OsmDataParsingException thrown if something went wrong. Check for chained
299 * exceptions.
300 */
301 public ChangesetDataSet parse(ProgressMonitor progressMonitor) throws OsmDataParsingException {
302 if (progressMonitor == null) {
303 progressMonitor = NullProgressMonitor.INSTANCE;
304 }
305 try {
306 progressMonitor.beginTask("");
307 progressMonitor.indeterminateSubTask(tr("Parsing changeset content ..."));
308 SAXParserFactory.newInstance().newSAXParser().parse(source, new Parser());
309 } catch(OsmDataParsingException e){
310 throw e;
311 } catch (ParserConfigurationException e) {
312 throw new OsmDataParsingException(e);
313 } catch(SAXException e) {
314 throw new OsmDataParsingException(e);
315 } catch(IOException e) {
316 throw new OsmDataParsingException(e);
317 } finally {
318 progressMonitor.finishTask();
319 }
320 return data;
321 }
322
323 /**
324 * Parses the content from the input source
325 *
326 * @return the parsed data
327 * @throws OsmDataParsingException thrown if something went wrong. Check for chained
328 * exceptions.
329 */
330 public ChangesetDataSet parse() throws OsmDataParsingException {
331 return parse(null);
332 }
333}
Note: See TracBrowser for help on using the repository browser.