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

Last change on this file since 215 was 215, checked in by imi, 17 years ago
  • fixed NPE in marker layer when marker type was not found
  • added one zoom to->data when first opening some data (since auto scale feature has been removed)
  • fixed NPE in preferences when there is a .jar without a manifest in plugin directory
File size: 7.0 KB
Line 
1package org.openstreetmap.josm.tools;
2
3import java.io.Reader;
4import java.lang.reflect.Field;
5import java.lang.reflect.Method;
6import java.lang.reflect.Modifier;
7import java.util.HashMap;
8import java.util.Iterator;
9import java.util.Map;
10import java.util.NoSuchElementException;
11import java.util.Stack;
12import java.util.concurrent.ArrayBlockingQueue;
13import java.util.concurrent.BlockingQueue;
14
15import org.xml.sax.Attributes;
16import org.xml.sax.SAXException;
17
18import uk.co.wilson.xml.MinML2;
19
20/**
21 * An helper class that reads from a XML stream into specific objects.
22 *
23 * @author Imi
24 */
25public class XmlObjectParser implements Iterable<Object> {
26
27 public static class Uniform<T> implements Iterable<T>{
28 private Iterator<Object> iterator;
29 /**
30 * @param klass This has to be specified since generics are ereased from
31 * class files so the JVM cannot deduce T itself.
32 */
33 public Uniform(Reader input, String tagname, Class<T> klass) {
34 XmlObjectParser parser = new XmlObjectParser();
35 parser.map(tagname, klass);
36 parser.start(input);
37 iterator = parser.iterator();
38 }
39 public Iterator<T> iterator() {
40 return new Iterator<T>(){
41 public boolean hasNext() {return iterator.hasNext();}
42 @SuppressWarnings("unchecked") public T next() {return (T)iterator.next();}
43 public void remove() {iterator.remove();}
44 };
45 }
46 }
47
48 private class Parser extends MinML2 {
49 Stack<Object> current = new Stack<Object>();
50 String characters = "";
51 @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
52 if (mapping.containsKey(qname)) {
53 Class<?> klass = mapping.get(qname).klass;
54 try {
55 current.push(klass.newInstance());
56 } catch (Exception e) {
57 throw new SAXException(e);
58 }
59 for (int i = 0; i < a.getLength(); ++i)
60 setValue(a.getQName(i), a.getValue(i));
61 if (mapping.get(qname).onStart)
62 report();
63 }
64 }
65 @Override public void endElement(String ns, String lname, String qname) throws SAXException {
66 if (mapping.containsKey(qname) && !mapping.get(qname).onStart)
67 report();
68 else if (characters != null && !current.isEmpty())
69 setValue(qname, characters);
70 }
71 @Override public void characters(char[] ch, int start, int length) {
72 String s = new String(ch, start, length);
73 characters += s;
74 }
75
76 private void report() {
77 try {
78 queue.put(current.pop());
79 } catch (InterruptedException e) {
80 }
81 characters = "";
82 }
83
84 private Object getValueForClass(Class<?> klass, String value) {
85 if (klass == Boolean.TYPE)
86 return parseBoolean(value);
87 else if (klass == Integer.TYPE || klass == Long.TYPE)
88 return Long.parseLong(value);
89 else if (klass == Float.TYPE || klass == Double.TYPE)
90 return Double.parseDouble(value);
91 return value;
92 }
93
94 private void setValue(String fieldName, String value) throws SAXException {
95 if (fieldName.equals("class") || fieldName.equals("default") || fieldName.equals("throw") || fieldName.equals("new") || fieldName.equals("null"))
96 fieldName += "_";
97 try {
98 Object c = current.peek();
99 Field f = null;
100 try {
101 f = c.getClass().getField(fieldName);
102 } catch (NoSuchFieldException e) {
103 }
104 if (f != null && Modifier.isPublic(f.getModifiers()))
105 f.set(c, getValueForClass(f.getType(), value));
106 else {
107 fieldName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
108 Method[] methods = c.getClass().getDeclaredMethods();
109 for (Method m : methods) {
110 if (m.getName().equals(fieldName) && m.getParameterTypes().length == 1) {
111 m.invoke(c, new Object[]{getValueForClass(m.getParameterTypes()[0], value)});
112 return;
113 }
114 }
115 }
116 } catch (Exception e) {
117 e.printStackTrace(); // SAXException does not dump inner exceptions.
118 throw new SAXException(e);
119 }
120 }
121 private boolean parseBoolean(String s) {
122 return s != null &&
123 !s.equals("0") &&
124 !s.startsWith("off") &&
125 !s.startsWith("false") &&
126 !s.startsWith("no");
127 }
128 }
129
130 private static class Entry {
131 Class<?> klass;
132 boolean onStart;
133 public Entry(Class<?> klass, boolean onStart) {
134 super();
135 this.klass = klass;
136 this.onStart = onStart;
137 }
138 }
139
140 private Map<String, Entry> mapping = new HashMap<String, Entry>();
141 private Parser parser;
142
143 /**
144 * The queue of already parsed items from the parsing thread.
145 */
146 private BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10);
147
148 /**
149 * This stores one item retrieved from the queue to give hasNext a chance.
150 * So this is also the object that will be returned on the next call to next().
151 */
152 private Object lookAhead = null;
153
154 /**
155 * This object represent the end of the stream (null is not allowed as
156 * member in class Queue).
157 */
158 private Object EOS = new Object();
159
160 public XmlObjectParser() {
161 parser = new Parser();
162 }
163
164 public Iterable<Object> start(final Reader in) {
165 new Thread(){
166 @Override public void run() {
167 try {
168 parser.parse(in);
169 } catch (Exception e) {
170 try {
171 queue.put(e);
172 } catch (InterruptedException e1) {
173 }
174 }
175 parser = null;
176 try {
177 queue.put(EOS);
178 } catch (InterruptedException e) {
179 }
180 }
181 }.start();
182 return this;
183 }
184
185 public void map(String tagName, Class<?> klass) {
186 mapping.put(tagName, new Entry(klass,false));
187 }
188
189 public void mapOnStart(String tagName, Class<?> klass) {
190 mapping.put(tagName, new Entry(klass,true));
191 }
192
193 /**
194 * @return The next object from the xml stream or <code>null</code>,
195 * if no more objects.
196 */
197 public Object next() throws SAXException {
198 fillLookAhead();
199 if (lookAhead == EOS)
200 throw new NoSuchElementException();
201 Object o = lookAhead;
202 lookAhead = null;
203 return o;
204 }
205
206 private void fillLookAhead() throws SAXException {
207 if (lookAhead != null)
208 return;
209 try {
210 lookAhead = queue.take();
211 if (lookAhead instanceof SAXException)
212 throw (SAXException)lookAhead;
213 else if (lookAhead instanceof RuntimeException)
214 throw (RuntimeException)lookAhead;
215 else if (lookAhead instanceof Exception)
216 throw new SAXException((Exception)lookAhead);
217 } catch (InterruptedException e) {
218 throw new RuntimeException("XmlObjectParser must not be interrupted.", e);
219 }
220 }
221
222 public boolean hasNext() throws SAXException {
223 fillLookAhead();
224 return lookAhead != EOS;
225 }
226
227 public Iterator<Object> iterator() {
228 return new Iterator<Object>(){
229 public boolean hasNext() {
230 try {
231 return XmlObjectParser.this.hasNext();
232 } catch (SAXException e) {
233 e.printStackTrace();
234 throw new RuntimeException(e);
235 }
236 }
237 public Object next() {
238 try {
239 return XmlObjectParser.this.next();
240 } catch (SAXException e) {
241 e.printStackTrace();
242 throw new RuntimeException(e);
243 }
244 }
245 public void remove() {
246 throw new UnsupportedOperationException();
247 }
248 };
249 }
250}
Note: See TracBrowser for help on using the repository browser.