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

Last change on this file since 11296 was 11100, checked in by Don-vip, 8 years ago

sonar - squid:S2148 - Underscores should be used to make large numbers readable

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