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.InputStream; 007import java.util.Arrays; 008 009import javax.xml.stream.XMLStreamConstants; 010import javax.xml.stream.XMLStreamException; 011 012import org.openstreetmap.josm.data.coor.LatLon; 013import org.openstreetmap.josm.data.osm.DataSet; 014import org.openstreetmap.josm.data.osm.NoteData; 015import org.openstreetmap.josm.data.osm.OsmPrimitive; 016import org.openstreetmap.josm.gui.progress.ProgressMonitor; 017import org.openstreetmap.josm.tools.Pair; 018 019/** 020 * Reader for <a href="http://wiki.openstreetmap.org/wiki/OsmChange">OsmChange</a> file format. 021 */ 022public class OsmChangeReader extends OsmReader { 023 024 /** 025 * List of possible actions. 026 */ 027 private static final String[] ACTIONS = {"create", "modify", "delete"}; 028 029 protected final NoteData noteData = new NoteData(); 030 031 /** 032 * constructor (for private and subclasses use only) 033 * 034 * @see #parseDataSet(InputStream, ProgressMonitor) 035 */ 036 protected OsmChangeReader() { 037 // Restricts visibility 038 } 039 040 @Override 041 protected void parseRoot() throws XMLStreamException { 042 if ("osmChange".equals(parser.getLocalName())) { 043 parseOsmChange(); 044 } else { 045 parseUnknown(); 046 } 047 } 048 049 private void parseOsmChange() throws XMLStreamException { 050 String v = parser.getAttributeValue(null, "version"); 051 if (v == null) { 052 throwException(tr("Missing mandatory attribute ''{0}''.", "version")); 053 } 054 if (!"0.6".equals(v)) { 055 throwException(tr("Unsupported version: {0}", v)); 056 } 057 ds.setVersion(v); 058 while (parser.hasNext()) { 059 int event = parser.next(); 060 if (event == XMLStreamConstants.START_ELEMENT) { 061 if (Arrays.asList(ACTIONS).contains(parser.getLocalName())) { 062 parseCommon(parser.getLocalName()); 063 } else { 064 parseUnknown(); 065 } 066 } else if (event == XMLStreamConstants.END_ELEMENT) { 067 return; 068 } 069 } 070 } 071 072 private void parseCommon(String action) throws XMLStreamException { 073 while (parser.hasNext()) { 074 int event = parser.next(); 075 if (event == XMLStreamConstants.START_ELEMENT) { 076 OsmPrimitive p = null; 077 switch (parser.getLocalName()) { 078 case "node": 079 p = parseNode(); 080 break; 081 case "way": 082 p = parseWay(); 083 break; 084 case "relation": 085 p = parseRelation(); 086 break; 087 case "note": 088 parseNote(); 089 break; 090 default: 091 parseUnknown(); 092 } 093 if (p != null && action != null) { 094 if ("modify".equals(action)) { 095 p.setModified(true); 096 } else if ("delete".equals(action)) { 097 p.setDeleted(true); 098 } 099 } 100 } else if (event == XMLStreamConstants.END_ELEMENT) { 101 return; 102 } 103 } 104 } 105 106 private void parseNote() throws XMLStreamException { 107 LatLon location = NoteReader.parseLatLon(s -> parser.getAttributeValue(null, s)); 108 String text = null; 109 while (parser.hasNext()) { 110 int event = parser.next(); 111 if (event == XMLStreamConstants.START_ELEMENT) { 112 switch (parser.getLocalName()) { 113 case "comment": 114 text = parser.getAttributeValue(null, "text"); 115 jumpToEnd(); 116 break; 117 default: 118 parseUnknown(); 119 } 120 } else if (event == XMLStreamConstants.END_ELEMENT) { 121 break; 122 } 123 } 124 if (location != null && text != null && !text.isEmpty()) { 125 noteData.createNote(location, text); 126 } 127 } 128 129 /** 130 * Replies the parsed notes data. 131 * @return the parsed notes data 132 * @since 14101 133 */ 134 public final NoteData getNoteData() { 135 return noteData; 136 } 137 138 /** 139 * Parse the given input source and return the dataset. 140 * 141 * @param source the source input stream. Must not be <code>null</code>. 142 * @param progressMonitor the progress monitor. If <code>null</code>, 143 * {@link org.openstreetmap.josm.gui.progress.NullProgressMonitor#INSTANCE} is assumed 144 * 145 * @return the dataset with the parsed data 146 * @throws IllegalDataException if the an error was found while parsing the data from the source 147 * @throws IllegalArgumentException if source is <code>null</code> 148 */ 149 public static DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException { 150 return new OsmChangeReader().doParseDataSet(source, progressMonitor); 151 } 152 153 /** 154 * Parse the given input source and return the dataset and notes, if any (OsmAnd extends the osmChange format by adding notes). 155 * 156 * @param source the source input stream. Must not be <code>null</code>. 157 * @param progressMonitor the progress monitor. If <code>null</code>, 158 * {@link org.openstreetmap.josm.gui.progress.NullProgressMonitor#INSTANCE} is assumed 159 * 160 * @return the dataset with the parsed data 161 * @throws IllegalDataException if the an error was found while parsing the data from the source 162 * @throws IllegalArgumentException if source is <code>null</code> 163 * @since 14101 164 */ 165 public static Pair<DataSet, NoteData> parseDataSetAndNotes(InputStream source, ProgressMonitor progressMonitor) 166 throws IllegalDataException { 167 OsmChangeReader osmChangeReader = new OsmChangeReader(); 168 osmChangeReader.doParseDataSet(source, progressMonitor); 169 return new Pair<>(osmChangeReader.getDataSet(), osmChangeReader.getNoteData()); 170 } 171}