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

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