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

Last change on this file since 729 was 679, checked in by stoecker, 16 years ago

finished XML based translations of presets fixes #960
correct bounding box illegal access fixes #1044
do no longer modify objects when unnessesary (can result in 0 objects
modified in Undo :-)
correct typo fixes #730
some I18N corrections

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