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

Last change on this file since 1724 was 1440, checked in by stoecker, 15 years ago

close #2230. patch by xeen

  • Property svn:eol-style set to native
File size: 10.0 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
23import org.openstreetmap.josm.Main;
24
25/**
26 * An helper class that reads from a XML stream into specific objects.
27 *
28 * @author Imi
29 */
30public class XmlObjectParser implements Iterable<Object> {
31
32 public static final String lang = Main.getLanguageCode()+".";
33 public static class Uniform<T> implements Iterable<T>{
34 private Iterator<Object> iterator;
35 /**
36 * @param klass This has to be specified since generics are ereased from
37 * class files so the JVM cannot deduce T itself.
38 */
39 public Uniform(Reader input, String tagname, Class<T> klass) {
40 XmlObjectParser parser = new XmlObjectParser();
41 parser.map(tagname, klass);
42 parser.start(input);
43 iterator = parser.iterator();
44 }
45 public Iterator<T> iterator() {
46 return new Iterator<T>(){
47 public boolean hasNext() {return iterator.hasNext();}
48 @SuppressWarnings("unchecked") public T next() {return (T)iterator.next();}
49 public void remove() {iterator.remove();}
50 };
51 }
52 }
53
54 private class Parser extends DefaultHandler {
55 Stack<Object> current = new Stack<Object>();
56 String characters = "";
57 @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
58 if (mapping.containsKey(qname)) {
59 Class<?> klass = mapping.get(qname).klass;
60 try {
61 current.push(klass.newInstance());
62 } catch (Exception e) {
63 throw new SAXException(e);
64 }
65 for (int i = 0; i < a.getLength(); ++i)
66 setValue(a.getQName(i), a.getValue(i));
67 if (mapping.get(qname).onStart)
68 report();
69 if (mapping.get(qname).both)
70 {
71 try {
72 queue.put(current.peek());
73 } catch (InterruptedException e) {
74 }
75 }
76 }
77 }
78 @Override public void endElement(String ns, String lname, String qname) throws SAXException {
79 if (mapping.containsKey(qname) && !mapping.get(qname).onStart)
80 report();
81 else if (characters != null && !current.isEmpty()) {
82 setValue(qname, characters.trim());
83 characters = "";
84 }
85 }
86 @Override public void characters(char[] ch, int start, int length) {
87 String s = new String(ch, start, length);
88 characters += s;
89 }
90
91 private void report() {
92 try {
93 queue.put(current.pop());
94 } catch (InterruptedException e) {
95 }
96 characters = "";
97 }
98
99 private Object getValueForClass(Class<?> klass, String value) {
100 if (klass == Boolean.TYPE)
101 return parseBoolean(value);
102 else if (klass == Integer.TYPE || klass == Long.TYPE)
103 return Long.parseLong(value);
104 else if (klass == Float.TYPE || klass == Double.TYPE)
105 return Double.parseDouble(value);
106 return value;
107 }
108
109 private void setValue(String fieldName, String value) throws SAXException {
110 if (fieldName.equals("class") || fieldName.equals("default") || fieldName.equals("throw") || fieldName.equals("new") || fieldName.equals("null"))
111 fieldName += "_";
112 try {
113 Object c = current.peek();
114 Field f = null;
115 try {
116 f = c.getClass().getField(fieldName);
117 } catch (NoSuchFieldException e) {
118 if(fieldName.startsWith(lang))
119 {
120 String locfieldName = "locale_" +
121 fieldName.substring(lang.length());
122 try {
123 f = c.getClass().getField(locfieldName);
124 } catch (NoSuchFieldException ex) {
125 }
126 }
127 }
128 if (f != null && Modifier.isPublic(f.getModifiers()))
129 f.set(c, getValueForClass(f.getType(), value));
130 else {
131 if(fieldName.startsWith(lang))
132 {
133 int l = lang.length();
134 fieldName = "set" + fieldName.substring(l,l+1).toUpperCase() + fieldName.substring(l+1);
135 }
136 else
137 {
138 fieldName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
139 }
140 Method[] methods = c.getClass().getDeclaredMethods();
141 for (Method m : methods) {
142 if (m.getName().equals(fieldName) && m.getParameterTypes().length == 1) {
143 m.invoke(c, new Object[]{getValueForClass(m.getParameterTypes()[0], value)});
144 return;
145 }
146 }
147 }
148 } catch (Exception e) {
149 e.printStackTrace(); // SAXException does not dump inner exceptions.
150 throw new SAXException(e);
151 }
152 }
153 private boolean parseBoolean(String s) {
154 return s != null &&
155 !s.equals("0") &&
156 !s.startsWith("off") &&
157 !s.startsWith("false") &&
158 !s.startsWith("no");
159 }
160 }
161
162 private static class Entry {
163 Class<?> klass;
164 boolean onStart;
165 boolean both;
166 public Entry(Class<?> klass, boolean onStart, boolean both) {
167 super();
168 this.klass = klass;
169 this.onStart = onStart;
170 this.both = both;
171 }
172 }
173
174 private Map<String, Entry> mapping = new HashMap<String, Entry>();
175 private Parser parser;
176
177 /**
178 * The queue of already parsed items from the parsing thread.
179 */
180 private BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(10);
181
182 /**
183 * This stores one item retrieved from the queue to give hasNext a chance.
184 * So this is also the object that will be returned on the next call to next().
185 */
186 private Object lookAhead = null;
187
188 /**
189 * This object represent the end of the stream (null is not allowed as
190 * member in class Queue).
191 */
192 private Object EOS = new Object();
193
194 public XmlObjectParser() {
195 parser = new Parser();
196 }
197
198 public Iterable<Object> start(final Reader in) {
199 new Thread("XML Reader"){
200 @Override public void run() {
201 try {
202 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(in), parser);
203 } catch (Exception e) {
204 try {
205 queue.put(e);
206 } catch (InterruptedException e1) {
207 }
208 }
209 parser = null;
210 try {
211 queue.put(EOS);
212 } catch (InterruptedException e) {
213 }
214 }
215 }.start();
216 return this;
217 }
218
219 public void map(String tagName, Class<?> klass) {
220 mapping.put(tagName, new Entry(klass,false,false));
221 }
222
223 public void mapOnStart(String tagName, Class<?> klass) {
224 mapping.put(tagName, new Entry(klass,true,false));
225 }
226
227 public void mapBoth(String tagName, Class<?> klass) {
228 mapping.put(tagName, new Entry(klass,false,true));
229 }
230
231 /**
232 * @return The next object from the xml stream or <code>null</code>,
233 * if no more objects.
234 */
235 public Object next() throws SAXException {
236 fillLookAhead();
237 if (lookAhead == EOS)
238 throw new NoSuchElementException();
239 Object o = lookAhead;
240 lookAhead = null;
241 return o;
242 }
243
244 private void fillLookAhead() throws SAXException {
245 if (lookAhead != null)
246 return;
247 try {
248 lookAhead = queue.take();
249 if (lookAhead instanceof SAXException)
250 throw (SAXException)lookAhead;
251 else if (lookAhead instanceof RuntimeException)
252 throw (RuntimeException)lookAhead;
253 else if (lookAhead instanceof Exception)
254 throw new SAXException((Exception)lookAhead);
255 } catch (InterruptedException e) {
256 throw new RuntimeException("XmlObjectParser must not be interrupted.", e);
257 }
258 }
259
260 public boolean hasNext() throws SAXException {
261 fillLookAhead();
262 return lookAhead != EOS;
263 }
264
265 public Iterator<Object> iterator() {
266 return new Iterator<Object>(){
267 public boolean hasNext() {
268 try {
269 return XmlObjectParser.this.hasNext();
270 } catch (SAXException e) {
271 e.printStackTrace();
272 throw new RuntimeException(e);
273 }
274 }
275 public Object next() {
276 try {
277 return XmlObjectParser.this.next();
278 } catch (SAXException e) {
279 e.printStackTrace();
280 throw new RuntimeException(e);
281 }
282 }
283 public void remove() {
284 throw new UnsupportedOperationException();
285 }
286 };
287 }
288}
Note: See TracBrowser for help on using the repository browser.