source: josm/trunk/src/org/openstreetmap/josm/io/imagery/ImageryReader.java@ 7186

Last change on this file since 7186 was 7186, checked in by bastiK, 10 years ago

see #9914 - automatic update for imagery entries using id

File size: 10.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.imagery;
3
4import java.io.IOException;
5import java.io.InputStream;
6import java.util.ArrayList;
7import java.util.Arrays;
8import java.util.List;
9import java.util.Objects;
10import java.util.Stack;
11
12import javax.xml.parsers.ParserConfigurationException;
13import javax.xml.parsers.SAXParserFactory;
14
15import org.openstreetmap.josm.Main;
16import org.openstreetmap.josm.data.imagery.ImageryInfo;
17import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
18import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
19import org.openstreetmap.josm.data.imagery.Shape;
20import org.openstreetmap.josm.io.MirroredInputStream;
21import org.openstreetmap.josm.io.UTFInputStreamReader;
22import org.xml.sax.Attributes;
23import org.xml.sax.InputSource;
24import org.xml.sax.SAXException;
25import org.xml.sax.helpers.DefaultHandler;
26
27public class ImageryReader {
28
29 private String source;
30
31 private enum State {
32 INIT, // initial state, should always be at the bottom of the stack
33 IMAGERY, // inside the imagery element
34 ENTRY, // inside an entry
35 ENTRY_ATTRIBUTE, // note we are inside an entry attribute to collect the character data
36 PROJECTIONS,
37 CODE,
38 BOUNDS,
39 SHAPE,
40 UNKNOWN, // element is not recognized in the current context
41 }
42
43 public ImageryReader(String source) {
44 this.source = source;
45 }
46
47 public List<ImageryInfo> parse() throws SAXException, IOException {
48 Parser parser = new Parser();
49 try {
50 SAXParserFactory factory = SAXParserFactory.newInstance();
51 factory.setNamespaceAware(true);
52 try (InputStream in = new MirroredInputStream(source)) {
53 InputSource is = new InputSource(UTFInputStreamReader.create(in));
54 factory.newSAXParser().parse(is, parser);
55 return parser.entries;
56 }
57 } catch (SAXException e) {
58 throw e;
59 } catch (ParserConfigurationException e) {
60 Main.error(e); // broken SAXException chaining
61 throw new SAXException(e);
62 }
63 }
64
65 private static class Parser extends DefaultHandler {
66 private StringBuffer accumulator = new StringBuffer();
67
68 private Stack<State> states;
69
70 List<ImageryInfo> entries;
71
72 /**
73 * Skip the current entry because it has mandatory attributes
74 * that this version of JOSM cannot process.
75 */
76 boolean skipEntry;
77
78 ImageryInfo entry;
79 ImageryBounds bounds;
80 Shape shape;
81 List<String> projections;
82
83 @Override public void startDocument() {
84 accumulator = new StringBuffer();
85 skipEntry = false;
86 states = new Stack<>();
87 states.push(State.INIT);
88 entries = new ArrayList<>();
89 entry = null;
90 bounds = null;
91 projections = null;
92 }
93
94 @Override
95 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
96 accumulator.setLength(0);
97 State newState = null;
98 switch (states.peek()) {
99 case INIT:
100 if ("imagery".equals(qName)) {
101 newState = State.IMAGERY;
102 }
103 break;
104 case IMAGERY:
105 if ("entry".equals(qName)) {
106 entry = new ImageryInfo();
107 skipEntry = false;
108 newState = State.ENTRY;
109 }
110 break;
111 case ENTRY:
112 if (Arrays.asList(new String[] {
113 "name",
114 "id",
115 "type",
116 "default",
117 "url",
118 "eula",
119 "min-zoom",
120 "max-zoom",
121 "attribution-text",
122 "attribution-url",
123 "logo-image",
124 "logo-url",
125 "terms-of-use-text",
126 "terms-of-use-url",
127 "country-code",
128 "icon",
129 }).contains(qName)) {
130 newState = State.ENTRY_ATTRIBUTE;
131 } else if ("bounds".equals(qName)) {
132 try {
133 bounds = new ImageryBounds(
134 atts.getValue("min-lat") + "," +
135 atts.getValue("min-lon") + "," +
136 atts.getValue("max-lat") + "," +
137 atts.getValue("max-lon"), ",");
138 } catch (IllegalArgumentException e) {
139 break;
140 }
141 newState = State.BOUNDS;
142 } else if ("projections".equals(qName)) {
143 projections = new ArrayList<>();
144 newState = State.PROJECTIONS;
145 }
146 break;
147 case BOUNDS:
148 if ("shape".equals(qName)) {
149 shape = new Shape();
150 newState = State.SHAPE;
151 }
152 break;
153 case SHAPE:
154 if ("point".equals(qName)) {
155 try {
156 shape.addPoint(atts.getValue("lat"), atts.getValue("lon"));
157 } catch (IllegalArgumentException e) {
158 break;
159 }
160 }
161 break;
162 case PROJECTIONS:
163 if ("code".equals(qName)) {
164 newState = State.CODE;
165 }
166 break;
167 }
168 /**
169 * Did not recognize the element, so the new state is UNKNOWN.
170 * This includes the case where we are already inside an unknown
171 * element, i.e. we do not try to understand the inner content
172 * of an unknown element, but wait till it's over.
173 */
174 if (newState == null) {
175 newState = State.UNKNOWN;
176 }
177 states.push(newState);
178 if (newState == State.UNKNOWN && "true".equals(atts.getValue("mandatory"))) {
179 skipEntry = true;
180 }
181 return;
182 }
183
184 @Override
185 public void characters(char[] ch, int start, int length) {
186 accumulator.append(ch, start, length);
187 }
188
189 @Override
190 public void endElement(String namespaceURI, String qName, String rqName) {
191 switch (states.pop()) {
192 case INIT:
193 throw new RuntimeException("parsing error: more closing than opening elements");
194 case ENTRY:
195 if ("entry".equals(qName)) {
196 if (!skipEntry) {
197 entries.add(entry);
198 }
199 entry = null;
200 }
201 break;
202 case ENTRY_ATTRIBUTE:
203 switch(qName) {
204 case "name":
205 entry.setTranslatedName(accumulator.toString());
206 break;
207 case "id":
208 entry.setId(accumulator.toString());
209 break;
210 case "type":
211 boolean found = false;
212 for (ImageryType type : ImageryType.values()) {
213 if (Objects.equals(accumulator.toString(), type.getTypeString())) {
214 entry.setImageryType(type);
215 found = true;
216 break;
217 }
218 }
219 if (!found) {
220 skipEntry = true;
221 }
222 break;
223 case "default":
224 switch (accumulator.toString()) {
225 case "true":
226 entry.setDefaultEntry(true);
227 break;
228 case "false":
229 entry.setDefaultEntry(false);
230 break;
231 default:
232 skipEntry = true;
233 }
234 break;
235 case "url":
236 entry.setUrl(accumulator.toString());
237 break;
238 case "eula":
239 entry.setEulaAcceptanceRequired(accumulator.toString());
240 break;
241 case "min-zoom":
242 case "max-zoom":
243 Integer val = null;
244 try {
245 val = Integer.parseInt(accumulator.toString());
246 } catch(NumberFormatException e) {
247 val = null;
248 }
249 if (val == null) {
250 skipEntry = true;
251 } else {
252 if ("min-zoom".equals(qName)) {
253 entry.setDefaultMinZoom(val);
254 } else {
255 entry.setDefaultMaxZoom(val);
256 }
257 }
258 break;
259 case "attribution-text":
260 entry.setAttributionText(accumulator.toString());
261 break;
262 case "attribution-url":
263 entry.setAttributionLinkURL(accumulator.toString());
264 break;
265 case "logo-image":
266 entry.setAttributionImage(accumulator.toString());
267 break;
268 case "logo-url":
269 entry.setAttributionImageURL(accumulator.toString());
270 break;
271 case "terms-of-use-text":
272 entry.setTermsOfUseText(accumulator.toString());
273 break;
274 case "terms-of-use-url":
275 entry.setTermsOfUseURL(accumulator.toString());
276 break;
277 case "country-code":
278 entry.setCountryCode(accumulator.toString());
279 break;
280 case "icon":
281 entry.setIcon(accumulator.toString());
282 break;
283 }
284 break;
285 case BOUNDS:
286 entry.setBounds(bounds);
287 bounds = null;
288 break;
289 case SHAPE:
290 bounds.addShape(shape);
291 shape = null;
292 break;
293 case CODE:
294 projections.add(accumulator.toString());
295 break;
296 case PROJECTIONS:
297 entry.setServerProjections(projections);
298 projections = null;
299 break;
300 }
301 }
302 }
303}
Note: See TracBrowser for help on using the repository browser.