source: josm/trunk/src/org/openstreetmap/josm/io/NoteReader.java@ 19050

Last change on this file since 19050 was 19050, checked in by taylor.smock, 4 weeks ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

  • Property svn:eol-style set to native
File size: 8.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import java.io.ByteArrayInputStream;
5import java.io.IOException;
6import java.io.InputStream;
7import java.nio.charset.StandardCharsets;
8import java.time.Instant;
9import java.util.ArrayList;
10import java.util.List;
11import java.util.Locale;
12import java.util.Optional;
13import java.util.function.UnaryOperator;
14
15import javax.xml.parsers.ParserConfigurationException;
16
17import org.openstreetmap.josm.data.coor.LatLon;
18import org.openstreetmap.josm.data.notes.Note;
19import org.openstreetmap.josm.data.notes.NoteComment;
20import org.openstreetmap.josm.data.notes.NoteComment.Action;
21import org.openstreetmap.josm.data.osm.User;
22import org.openstreetmap.josm.tools.Logging;
23import org.openstreetmap.josm.tools.XmlUtils;
24import org.openstreetmap.josm.tools.date.DateUtils;
25import org.xml.sax.Attributes;
26import org.xml.sax.InputSource;
27import org.xml.sax.SAXException;
28import org.xml.sax.helpers.DefaultHandler;
29
30/**
31 * Class to read Note objects from their XML representation. It can take
32 * either API style XML which starts with an "osm" tag or a planet dump
33 * style XML which starts with an "osm-notes" tag.
34 */
35public class NoteReader {
36
37 private final InputSource inputSource;
38 private List<Note> parsedNotes;
39
40 /**
41 * Notes can be represented in two XML formats. One is returned by the API
42 * while the other is used to generate the notes dump file. The parser
43 * needs to know which one it is handling.
44 */
45 private enum NoteParseMode {
46 API,
47 DUMP
48 }
49
50 /**
51 * SAX handler to read note information from its XML representation.
52 * Reads both API style and planet dump style formats.
53 */
54 private final class Parser extends DefaultHandler {
55
56 private NoteParseMode parseMode;
57 private final StringBuilder buffer = new StringBuilder();
58 private Note thisNote;
59 private long commentUid;
60 private String commentUsername;
61 private Action noteAction;
62 private Instant commentCreateDate;
63 private boolean commentIsNew;
64 private List<Note> notes;
65 private String commentText;
66
67 @Override
68 public void characters(char[] ch, int start, int length) throws SAXException {
69 buffer.append(ch, start, length);
70 }
71
72 @Override
73 public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
74 buffer.setLength(0);
75 switch (qName) {
76 case "osm":
77 parseMode = NoteParseMode.API;
78 notes = new ArrayList<>(100);
79 return;
80 case "osm-notes":
81 parseMode = NoteParseMode.DUMP;
82 notes = new ArrayList<>(10_000);
83 return;
84 default: // Keep going
85 }
86
87 if (parseMode == NoteParseMode.API) {
88 if ("note".equals(qName)) {
89 thisNote = parseNoteBasic(attrs);
90 }
91 return;
92 }
93
94 //The rest only applies for dump mode
95 switch (qName) {
96 case "note":
97 thisNote = parseNoteFull(attrs);
98 break;
99 case "comment":
100 commentUid = Long.parseLong(Optional.ofNullable(attrs.getValue("uid")).orElse("0"));
101 commentUsername = attrs.getValue("user");
102 noteAction = Action.valueOf(attrs.getValue("action").toUpperCase(Locale.ENGLISH));
103 commentCreateDate = DateUtils.parseInstant(attrs.getValue("timestamp"));
104 commentIsNew = Boolean.parseBoolean(Optional.ofNullable(attrs.getValue("is_new")).orElse("false"));
105 break;
106 default: // Do nothing
107 }
108 }
109
110 @Override
111 public void endElement(String namespaceURI, String localName, String qName) {
112 if (notes != null && "note".equals(qName)) {
113 notes.add(thisNote);
114 }
115 if ("comment".equals(qName)) {
116 final User commentUser;
117 if (commentUid == 0) {
118 commentUser = User.getAnonymous();
119 } else {
120 commentUser = User.createOsmUser(commentUid, commentUsername);
121 }
122 if (parseMode == NoteParseMode.API) {
123 commentIsNew = false;
124 }
125 if (parseMode == NoteParseMode.DUMP) {
126 commentText = buffer.toString();
127 }
128 thisNote.addComment(new NoteComment(commentCreateDate, commentUser, commentText, noteAction, commentIsNew));
129 commentUid = 0;
130 commentUsername = null;
131 commentCreateDate = null;
132 commentIsNew = false;
133 commentText = null;
134 }
135 if (parseMode == NoteParseMode.DUMP) {
136 return;
137 }
138
139 //the rest only applies to API mode
140 switch (qName) {
141 case "id":
142 thisNote.setId(Long.parseLong(buffer.toString()));
143 break;
144 case "status":
145 thisNote.setState(Note.State.valueOf(buffer.toString().toUpperCase(Locale.ENGLISH)));
146 break;
147 case "date_created":
148 thisNote.setCreatedAt(DateUtils.parseInstant(buffer.toString()));
149 break;
150 case "date_closed":
151 thisNote.setClosedAt(DateUtils.parseInstant(buffer.toString()));
152 break;
153 case "date":
154 commentCreateDate = DateUtils.parseInstant(buffer.toString());
155 break;
156 case "user":
157 commentUsername = buffer.toString();
158 break;
159 case "uid":
160 commentUid = Long.parseLong(buffer.toString());
161 break;
162 case "text":
163 commentText = buffer.toString();
164 buffer.setLength(0);
165 break;
166 case "action":
167 noteAction = Action.valueOf(buffer.toString().toUpperCase(Locale.ENGLISH));
168 break;
169 case "note": //nothing to do for comment or note, already handled above
170 case "comment":
171 break;
172 default: // Keep going (return)
173 }
174 }
175
176 @Override
177 public void endDocument() throws SAXException {
178 parsedNotes = notes;
179 }
180 }
181
182 static LatLon parseLatLon(UnaryOperator<String> attrs) {
183 final double lat = Double.parseDouble(attrs.apply("lat"));
184 final double lon = Double.parseDouble(attrs.apply("lon"));
185 return new LatLon(lat, lon);
186 }
187
188 static Note parseNoteBasic(Attributes attrs) {
189 return parseNoteBasic(attrs::getValue);
190 }
191
192 static Note parseNoteBasic(UnaryOperator<String> attrs) {
193 return new Note(parseLatLon(attrs));
194 }
195
196 static Note parseNoteFull(Attributes attrs) {
197 return parseNoteFull(attrs::getValue);
198 }
199
200 static Note parseNoteFull(UnaryOperator<String> attrs) {
201 final Note note = parseNoteBasic(attrs);
202 String id = attrs.apply("id");
203 if (id != null) {
204 note.setId(Long.parseLong(id));
205 }
206 String closedTimeStr = attrs.apply("closed_at");
207 if (closedTimeStr == null) { //no closed_at means the note is still open
208 note.setState(Note.State.OPEN);
209 } else {
210 note.setState(Note.State.CLOSED);
211 note.setClosedAt(DateUtils.parseInstant(closedTimeStr));
212 }
213 String createdAt = attrs.apply("created_at");
214 if (createdAt != null) {
215 note.setCreatedAt(DateUtils.parseInstant(createdAt));
216 }
217 return note;
218 }
219
220 /**
221 * Initializes the reader with a given InputStream
222 * @param source - InputStream containing Notes XML
223 */
224 public NoteReader(InputStream source) {
225 this.inputSource = new InputSource(source);
226 }
227
228 /**
229 * Initializes the reader with a string as a source
230 * @param source UTF-8 string containing Notes XML to parse
231 */
232 public NoteReader(String source) {
233 this.inputSource = new InputSource(new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)));
234 }
235
236 /**
237 * Parses the InputStream given to the constructor and returns
238 * the resulting Note objects
239 * @return List of Notes parsed from the input data
240 * @throws SAXException if any SAX parsing error occurs
241 * @throws IOException if any I/O error occurs
242 */
243 public List<Note> parse() throws SAXException, IOException {
244 DefaultHandler parser = new Parser();
245 try {
246 XmlUtils.parseSafeSAX(inputSource, parser);
247 } catch (ParserConfigurationException e) {
248 Logging.error(e); // broken SAXException chaining
249 throw new SAXException(e);
250 }
251 return parsedNotes;
252 }
253}
Note: See TracBrowser for help on using the repository browser.