Ticket #7845: OsmChangesetContentParser.java

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