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

Last change on this file since 11570 was 11553, checked in by Don-vip, 7 years ago

refactor handling of null values - use Java 8 Optional where possible

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