source: josm/trunk/src/org/openstreetmap/josm/tools/XmlObjectParser.java@ 2702

Last change on this file since 2702 was 2676, checked in by jttt, 14 years ago

Fix some of the warnings found by FindBugs

  • Property svn:eol-style set to native
File size: 12.0 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.Reader;
7import java.lang.reflect.Field;
8import java.lang.reflect.Method;
9import java.lang.reflect.Modifier;
10import java.util.HashMap;
11import java.util.Iterator;
12import java.util.Map;
13import java.util.NoSuchElementException;
14import java.util.Stack;
15import java.util.concurrent.ArrayBlockingQueue;
16import java.util.concurrent.BlockingQueue;
17
18import javax.xml.parsers.SAXParserFactory;
19
20import org.xml.sax.Attributes;
21import org.xml.sax.InputSource;
22import org.xml.sax.Locator;
23import org.xml.sax.SAXException;
24import org.xml.sax.SAXParseException;
25import org.xml.sax.helpers.DefaultHandler;
26
27/**
28 * An helper class that reads from a XML stream into specific objects.
29 *
30 * @author Imi
31 */
32public class XmlObjectParser implements Iterable<Object> {
33 public static class PresetParsingException extends SAXException {
34 private int columnNumber;
35 private int lineNumber;
36
37 public PresetParsingException() {
38 super();
39 }
40
41 public PresetParsingException(Exception e) {
42 super(e);
43 }
44
45 public PresetParsingException(String message, Exception e) {
46 super(message, e);
47 }
48
49 public PresetParsingException(String message) {
50 super(message);
51 }
52
53 public PresetParsingException rememberLocation(Locator locator) {
54 if (locator == null) return this;
55 this.columnNumber = locator.getColumnNumber();
56 this.lineNumber = locator.getLineNumber();
57 return this;
58 }
59
60 @Override
61 public String getMessage() {
62 String msg = super.getMessage();
63 if (lineNumber == 0 && columnNumber == 0)
64 return msg;
65 if (msg == null) {
66 msg = getClass().getName();
67 }
68 msg = msg + " " + tr("(at line {0}, column {1})", lineNumber, columnNumber);
69 return msg;
70 }
71
72 public int getColumnNumber() {
73 return columnNumber;
74 }
75
76 public int getLineNumber() {
77 return lineNumber;
78 }
79 }
80
81 public static final String lang = LanguageInfo.getLanguageCodeXML();
82 public static class Uniform<T> implements Iterable<T>{
83 private Iterator<Object> iterator;
84 /**
85 * @param klass This has to be specified since generics are ereased from
86 * class files so the JVM cannot deduce T itself.
87 */
88 public Uniform(Reader input, String tagname, Class<T> klass) {
89 XmlObjectParser parser = new XmlObjectParser();
90 parser.map(tagname, klass);
91 parser.start(input);
92 iterator = parser.iterator();
93 }
94 public Iterator<T> iterator() {
95 return new Iterator<T>(){
96 public boolean hasNext() {return iterator.hasNext();}
97 @SuppressWarnings("unchecked") public T next() {return (T)iterator.next();}
98 public void remove() {iterator.remove();}
99 };
100 }
101 }
102
103 private class Parser extends DefaultHandler {
104 Stack<Object> current = new Stack<Object>();
105 String characters = "";
106
107 private Locator locator;
108
109 @Override
110 public void setDocumentLocator(Locator locator) {
111 this.locator = locator;
112 }
113
114 protected void throwException(Exception e) throws PresetParsingException{
115 throw new PresetParsingException(e).rememberLocation(locator);
116 }
117
118 @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
119 if (mapping.containsKey(qname)) {
120 Class<?> klass = mapping.get(qname).klass;
121 try {
122 current.push(klass.newInstance());
123 } catch (Exception e) {
124 throwException(e);
125 }
126 for (int i = 0; i < a.getLength(); ++i) {
127 setValue(a.getQName(i), a.getValue(i));
128 }
129 if (mapping.get(qname).onStart) {
130 report();
131 }
132 if (mapping.get(qname).both)
133 {
134 try {
135 queue.put(current.peek());
136 } catch (InterruptedException e) {
137 }
138 }
139 }
140 }
141 @Override public void endElement(String ns, String lname, String qname) throws SAXException {
142 if (mapping.containsKey(qname) && !mapping.get(qname).onStart) {
143 report();
144 } else if (characters != null && !current.isEmpty()) {
145 setValue(qname, characters.trim());
146 characters = "";
147 }
148 }
149 @Override public void characters(char[] ch, int start, int length) {
150 String s = new String(ch, start, length);
151 characters += s;
152 }
153
154 private void report() {
155 try {
156 queue.put(current.pop());
157 } catch (InterruptedException e) {
158 }
159 characters = "";
160 }
161
162 private Object getValueForClass(Class<?> klass, String value) {
163 if (klass == Boolean.TYPE)
164 return parseBoolean(value);
165 else if (klass == Integer.TYPE || klass == Long.TYPE)
166 return Long.parseLong(value);
167 else if (klass == Float.TYPE || klass == Double.TYPE)
168 return Double.parseDouble(value);
169 return value;
170 }
171
172 private void setValue(String fieldName, String value) throws SAXException {
173 if (fieldName.equals("class") || fieldName.equals("default") || fieldName.equals("throw") || fieldName.equals("new") || fieldName.equals("null")) {
174 fieldName += "_";
175 }
176 try {
177 Object c = current.peek();
178 Field f = null;
179 try {
180 f = c.getClass().getField(fieldName);
181 } catch (NoSuchFieldException e) {
182 if(fieldName.startsWith(lang))
183 {
184 String locfieldName = "locale_" +
185 fieldName.substring(lang.length());
186 try {
187 f = c.getClass().getField(locfieldName);
188 } catch (NoSuchFieldException ex) {
189 }
190 }
191 }
192 if (f != null && Modifier.isPublic(f.getModifiers())) {
193 f.set(c, getValueForClass(f.getType(), value));
194 } else {
195 if(fieldName.startsWith(lang))
196 {
197 int l = lang.length();
198 fieldName = "set" + fieldName.substring(l,l+1).toUpperCase() + fieldName.substring(l+1);
199 }
200 else
201 {
202 fieldName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
203 }
204 Method[] methods = c.getClass().getDeclaredMethods();
205 for (Method m : methods) {
206 if (m.getName().equals(fieldName) && m.getParameterTypes().length == 1) {
207 m.invoke(c, new Object[]{getValueForClass(m.getParameterTypes()[0], value)});
208 return;
209 }
210 }
211 }
212 } catch (Exception e) {
213 e.printStackTrace(); // SAXException does not dump inner exceptions.
214 throwException(e);
215 }
216 }
217
218 private boolean parseBoolean(String s) {
219 return s != null &&
220 !s.equals("0") &&
221 !s.startsWith("off") &&
222 !s.startsWith("false") &&
223 !s.startsWith("no");
224 }
225
226 @Override
227 public void error(SAXParseException e) throws SAXException {
228 throwException(e);
229 }
230
231 @Override
232 public void fatalError(SAXParseException e) throws SAXException {
233 throwException(e);
234 }
235 }
236
237 private static class Entry {
238 Class<?> klass;
239 boolean onStart;
240 boolean both;
241 public Entry(Class<?> klass, boolean onStart, boolean both) {
242 super();
243 this.klass = klass;
244 this.onStart = onStart;
245 this.both = both;
246 }
247 }
248
249 private Map<String, Entry> mapping = new HashMap<String, Entry>();
250 private Parser parser;
251
252 /**
253 * The queue of already parsed items from the parsing thread.
254 */
255 private BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10);
256
257 /**
258 * This stores one item retrieved from the queue to give hasNext a chance.
259 * So this is also the object that will be returned on the next call to next().
260 */
261 private Object lookAhead = null;
262
263 /**
264 * This object represent the end of the stream (null is not allowed as
265 * member in class Queue).
266 */
267 private Object EOS = new Object();
268
269 public XmlObjectParser() {
270 parser = new Parser();
271 }
272
273 public Iterable<Object> start(final Reader in) {
274 new Thread("XML Reader"){
275 @Override public void run() {
276 try {
277 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(in), parser);
278 } catch (Exception e) {
279 try {
280 queue.put(e);
281 } catch (InterruptedException e1) {
282 }
283 }
284 parser = null;
285 try {
286 queue.put(EOS);
287 } catch (InterruptedException e) {
288 }
289 }
290 }.start();
291 return this;
292 }
293
294 public void map(String tagName, Class<?> klass) {
295 mapping.put(tagName, new Entry(klass,false,false));
296 }
297
298 public void mapOnStart(String tagName, Class<?> klass) {
299 mapping.put(tagName, new Entry(klass,true,false));
300 }
301
302 public void mapBoth(String tagName, Class<?> klass) {
303 mapping.put(tagName, new Entry(klass,false,true));
304 }
305
306 /**
307 * @return The next object from the xml stream or <code>null</code>,
308 * if no more objects.
309 */
310 public Object next() throws SAXException {
311 fillLookAhead();
312 if (lookAhead == EOS)
313 throw new NoSuchElementException();
314 Object o = lookAhead;
315 lookAhead = null;
316 return o;
317 }
318
319 private void fillLookAhead() throws SAXException {
320 if (lookAhead != null)
321 return;
322 try {
323 lookAhead = queue.take();
324 if (lookAhead instanceof SAXException)
325 throw (SAXException)lookAhead;
326 else if (lookAhead instanceof RuntimeException)
327 throw (RuntimeException)lookAhead;
328 else if (lookAhead instanceof Exception)
329 throw new SAXException((Exception)lookAhead);
330 } catch (InterruptedException e) {
331 throw new RuntimeException("XmlObjectParser must not be interrupted.", e);
332 }
333 }
334
335 public boolean hasNext() throws SAXException {
336 fillLookAhead();
337 return lookAhead != EOS;
338 }
339
340 public Iterator<Object> iterator() {
341 return new Iterator<Object>(){
342 public boolean hasNext() {
343 try {
344 return XmlObjectParser.this.hasNext();
345 } catch (SAXException e) {
346 e.printStackTrace();
347 throw new RuntimeException(e);
348 }
349 }
350 public Object next() {
351 try {
352 return XmlObjectParser.this.next();
353 } catch (SAXException e) {
354 e.printStackTrace();
355 throw new RuntimeException(e);
356 }
357 }
358 public void remove() {
359 throw new UnsupportedOperationException();
360 }
361 };
362 }
363}
Note: See TracBrowser for help on using the repository browser.