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

Last change on this file since 8285 was 8285, checked in by Don-vip, 9 years ago

fix sonar squid:S2039 - Member variable visibility should be specified

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