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

Last change on this file since 4439 was 4439, checked in by bastiK, 13 years ago

change imagery projection tag to something neutral (wms 1.1.1 has <srs> and wms 1.3.0 has <crs>, but both is supported in principle)

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