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, 15 months 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.