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