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

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

sonar - fb-contrib - minor performance improvements:

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