001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.IOException; 007import java.io.InputStream; 008import java.io.InputStreamReader; 009import java.io.StringReader; 010import java.io.UnsupportedEncodingException; 011 012import javax.xml.parsers.ParserConfigurationException; 013import javax.xml.parsers.SAXParserFactory; 014 015import org.openstreetmap.josm.Main; 016import org.openstreetmap.josm.data.osm.ChangesetDataSet; 017import org.openstreetmap.josm.data.osm.ChangesetDataSet.ChangesetModificationType; 018import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 019import org.openstreetmap.josm.gui.progress.ProgressMonitor; 020import org.openstreetmap.josm.tools.CheckParameterUtil; 021import org.xml.sax.Attributes; 022import org.xml.sax.InputSource; 023import org.xml.sax.SAXException; 024import org.xml.sax.SAXParseException; 025 026/** 027 * Parser for OSM changeset content. 028 * @since 2688 029 */ 030public class OsmChangesetContentParser { 031 032 private InputSource source; 033 private final ChangesetDataSet data = new ChangesetDataSet(); 034 035 private class Parser extends AbstractParser { 036 037 /** the current change modification type */ 038 private ChangesetDataSet.ChangesetModificationType currentModificationType; 039 040 protected void throwException(String message) throws OsmDataParsingException { 041 throw new OsmDataParsingException(message).rememberLocation(locator); 042 } 043 044 protected void throwException(Exception e) throws OsmDataParsingException { 045 throw new OsmDataParsingException(e).rememberLocation(locator); 046 } 047 048 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 049 if (super.doStartElement(qName, atts)) { 050 // done 051 } else if (qName.equals("osmChange")) { 052 // do nothing 053 } else if (qName.equals("create")) { 054 currentModificationType = ChangesetModificationType.CREATED; 055 } else if (qName.equals("modify")) { 056 currentModificationType = ChangesetModificationType.UPDATED; 057 } else if (qName.equals("delete")) { 058 currentModificationType = ChangesetModificationType.DELETED; 059 } else { 060 Main.warn(tr("Unsupported start element ''{0}'' in changeset content at position ({1},{2}). Skipping.", qName, locator.getLineNumber(), locator.getColumnNumber())); 061 } 062 } 063 064 @Override 065 public void endElement(String uri, String localName, String qName) throws SAXException { 066 if (qName.equals("node") 067 || qName.equals("way") 068 || qName.equals("relation")) { 069 if (currentModificationType == null) { 070 throwException(tr("Illegal document structure. Found node, way, or relation outside of ''create'', ''modify'', or ''delete''.")); 071 } 072 data.put(currentPrimitive, currentModificationType); 073 } else if (qName.equals("osmChange")) { 074 // do nothing 075 } else if (qName.equals("create")) { 076 currentModificationType = null; 077 } else if (qName.equals("modify")) { 078 currentModificationType = null; 079 } else if (qName.equals("delete")) { 080 currentModificationType = null; 081 } else if (qName.equals("tag")) { 082 // do nothing 083 } else if (qName.equals("nd")) { 084 // do nothing 085 } else if (qName.equals("member")) { 086 // do nothing 087 } else { 088 Main.warn(tr("Unsupported end element ''{0}'' in changeset content at position ({1},{2}). Skipping.", qName, locator.getLineNumber(), locator.getColumnNumber())); 089 } 090 } 091 092 @Override 093 public void error(SAXParseException e) throws SAXException { 094 throwException(e); 095 } 096 097 @Override 098 public void fatalError(SAXParseException e) throws SAXException { 099 throwException(e); 100 } 101 } 102 103 /** 104 * Constructs a new {@code OsmChangesetContentParser}. 105 * 106 * @param source the input stream with the changeset content as XML document. Must not be null. 107 * @throws UnsupportedEncodingException if {@code UTF-8} charset is missing 108 * @throws IllegalArgumentException if source is {@code null}. 109 */ 110 public OsmChangesetContentParser(InputStream source) throws UnsupportedEncodingException { 111 CheckParameterUtil.ensureParameterNotNull(source, "source"); 112 this.source = new InputSource(new InputStreamReader(source, "UTF-8")); 113 } 114 115 /** 116 * Constructs a new {@code OsmChangesetContentParser}. 117 * 118 * @param source the input stream with the changeset content as XML document. Must not be null. 119 * @throws IllegalArgumentException if source is {@code null}. 120 */ 121 public OsmChangesetContentParser(String source) { 122 CheckParameterUtil.ensureParameterNotNull(source, "source"); 123 this.source = new InputSource(new StringReader(source)); 124 } 125 126 /** 127 * Parses the content. 128 * 129 * @param progressMonitor the progress monitor. Set to {@link NullProgressMonitor#INSTANCE} if null 130 * @return the parsed data 131 * @throws OsmDataParsingException thrown if something went wrong. Check for chained 132 * exceptions. 133 */ 134 public ChangesetDataSet parse(ProgressMonitor progressMonitor) throws OsmDataParsingException { 135 if (progressMonitor == null) { 136 progressMonitor = NullProgressMonitor.INSTANCE; 137 } 138 try { 139 progressMonitor.beginTask(""); 140 progressMonitor.indeterminateSubTask(tr("Parsing changeset content ...")); 141 SAXParserFactory.newInstance().newSAXParser().parse(source, new Parser()); 142 } catch(OsmDataParsingException e){ 143 throw e; 144 } catch (ParserConfigurationException e) { 145 throw new OsmDataParsingException(e); 146 } catch(SAXException e) { 147 throw new OsmDataParsingException(e); 148 } catch(IOException e) { 149 throw new OsmDataParsingException(e); 150 } finally { 151 progressMonitor.finishTask(); 152 } 153 return data; 154 } 155 156 /** 157 * Parses the content from the input source 158 * 159 * @return the parsed data 160 * @throws OsmDataParsingException thrown if something went wrong. Check for chained 161 * exceptions. 162 */ 163 public ChangesetDataSet parse() throws OsmDataParsingException { 164 return parse(null); 165 } 166}