001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.beans;
019:
020: import java.io.InputStream;
021: import java.lang.reflect.Array;
022: import java.lang.reflect.Field;
023: import java.lang.reflect.Method;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Stack;
027:
028: import javax.xml.parsers.SAXParserFactory;
029:
030: import org.xml.sax.Attributes;
031: import org.xml.sax.SAXException;
032: import org.xml.sax.SAXParseException;
033: import org.xml.sax.helpers.DefaultHandler;
034:
035: /**
036: * <code>XMLDecoder</code> reads objects from xml created by
037: * <code>XMLEncoder</code>.
038: * <p>
039: * The API is similar to <code>ObjectInputStream</code>.
040: * </p>
041: */
042: public class XMLDecoder {
043:
044: private ClassLoader defaultClassLoader = null;
045:
046: private static class DefaultExceptionListener implements
047: ExceptionListener {
048:
049: public void exceptionThrown(Exception e) {
050: e.printStackTrace();
051: System.err.println("Continue..."); //$NON-NLS-1$
052: }
053: }
054:
055: private class SAXHandler extends DefaultHandler {
056:
057: boolean inJavaElem = false;
058:
059: HashMap<String, Object> idObjMap = new HashMap<String, Object>();
060:
061: @Override
062: public void characters(char[] ch, int start, int length)
063: throws SAXException {
064: if (!inJavaElem) {
065: return;
066: }
067: if (readObjs.size() > 0) {
068: Elem elem = readObjs.peek();
069: if (elem.isBasicType) {
070: String str = new String(ch, start, length);
071: elem.methodName = elem.methodName == null ? str
072: : elem.methodName + str;
073: }
074: }
075: }
076:
077: @SuppressWarnings("nls")
078: @Override
079: public void startElement(String uri, String localName,
080: String qName, Attributes attributes)
081: throws SAXException {
082: if (!inJavaElem) {
083: if ("java".equals(qName)) {
084: inJavaElem = true;
085: } else {
086: listener.exceptionThrown(new Exception(
087: "unknown root element: " + qName));
088: }
089: return;
090: }
091:
092: if ("object".equals(qName)) {
093: startObjectElem(attributes);
094: } else if ("array".equals(qName)) {
095: startArrayElem(attributes);
096: } else if ("void".equals(qName)) {
097: startVoidElem(attributes);
098: } else if ("boolean".equals(qName) || "byte".equals(qName)
099: || "char".equals(qName) || "class".equals(qName)
100: || "double".equals(qName) || "float".equals(qName)
101: || "int".equals(qName) || "long".equals(qName)
102: || "short".equals(qName) || "string".equals(qName)
103: || "null".equals(qName)) {
104: startBasicElem(qName, attributes);
105: }
106: }
107:
108: @SuppressWarnings("nls")
109: private void startObjectElem(Attributes attributes) {
110: Elem elem = new Elem();
111: elem.isExpression = true;
112: elem.id = attributes.getValue("id");
113: elem.idref = attributes.getValue("idref");
114: if (elem.idref == null) {
115: obtainTarget(elem, attributes);
116: obtainMethod(elem, attributes);
117: }
118:
119: readObjs.push(elem);
120: }
121:
122: private void obtainTarget(Elem elem, Attributes attributes) {
123: String className = attributes.getValue("class"); //$NON-NLS-1$
124: if (className != null) {
125: try {
126: elem.target = classForName(className);
127: } catch (ClassNotFoundException e) {
128: listener.exceptionThrown(e);
129: }
130: } else {
131: Elem parent = latestUnclosedElem();
132: if (parent == null) {
133: elem.target = owner;
134: return;
135: }
136: elem.target = execute(parent);
137: }
138: }
139:
140: @SuppressWarnings("nls")
141: private void obtainMethod(Elem elem, Attributes attributes) {
142: elem.methodName = attributes.getValue("method");
143: if (elem.methodName != null) {
144: return;
145: }
146:
147: elem.methodName = attributes.getValue("property");
148: if (elem.methodName != null) {
149: elem.fromProperty = true;
150: return;
151: }
152:
153: elem.methodName = attributes.getValue("index");
154: if (elem.methodName != null) {
155: elem.fromIndex = true;
156: return;
157: }
158:
159: elem.methodName = attributes.getValue("field");
160: if (elem.methodName != null) {
161: elem.fromField = true;
162: return;
163: }
164:
165: elem.methodName = attributes.getValue("owner");
166: if (elem.methodName != null) {
167: elem.fromOwner = true;
168: return;
169: }
170:
171: elem.methodName = "new"; // default method name
172: }
173:
174: @SuppressWarnings("nls")
175: private Class<?> classForName(String className)
176: throws ClassNotFoundException {
177: if ("boolean".equals(className)) {
178: return Boolean.TYPE;
179: } else if ("byte".equals(className)) {
180: return Byte.TYPE;
181: } else if ("char".equals(className)) {
182: return Character.TYPE;
183: } else if ("double".equals(className)) {
184: return Double.TYPE;
185: } else if ("float".equals(className)) {
186: return Float.TYPE;
187: } else if ("int".equals(className)) {
188: return Integer.TYPE;
189: } else if ("long".equals(className)) {
190: return Long.TYPE;
191: } else if ("short".equals(className)) {
192: return Short.TYPE;
193: } else {
194: return Class.forName(className, true,
195: defaultClassLoader == null ? Thread
196: .currentThread()
197: .getContextClassLoader()
198: : defaultClassLoader);
199: }
200: }
201:
202: @SuppressWarnings("nls")
203: private void startArrayElem(Attributes attributes) {
204: Elem elem = new Elem();
205: elem.isExpression = true;
206: elem.id = attributes.getValue("id");
207: try {
208: // find component class
209: Class<?> compClass = classForName(attributes
210: .getValue("class"));
211: // find length
212: int length = Integer.parseInt(attributes
213: .getValue("length"));
214: // execute, new array instance
215: elem.result = Array.newInstance(compClass, length);
216: elem.isExecuted = true;
217: } catch (Exception e) {
218: listener.exceptionThrown(e);
219: }
220: readObjs.push(elem);
221: }
222:
223: @SuppressWarnings("nls")
224: private void startVoidElem(Attributes attributes) {
225: Elem elem = new Elem();
226: elem.id = attributes.getValue("id");
227: obtainTarget(elem, attributes);
228: obtainMethod(elem, attributes);
229: readObjs.push(elem);
230: }
231:
232: @SuppressWarnings("nls")
233: private void startBasicElem(String tagName,
234: Attributes attributes) {
235: Elem elem = new Elem();
236: elem.isBasicType = true;
237: elem.isExpression = true;
238: elem.id = attributes.getValue("id");
239: elem.idref = attributes.getValue("idref");
240: elem.target = tagName;
241: readObjs.push(elem);
242: }
243:
244: @Override
245: public void endElement(String uri, String localName,
246: String qName) throws SAXException {
247: if (!inJavaElem) {
248: return;
249: }
250: if ("java".equals(qName)) { //$NON-NLS-1$
251: inJavaElem = false;
252: return;
253: }
254: // find the elem to close
255: Elem toClose = latestUnclosedElem();
256: // make sure it is executed
257: execute(toClose);
258: // set to closed
259: toClose.isClosed = true;
260: // pop it and its children
261: while (readObjs.pop() != toClose) {
262: //
263: }
264: // push back expression
265: if (toClose.isExpression) {
266: readObjs.push(toClose);
267: }
268: }
269:
270: private Elem latestUnclosedElem() {
271: for (int i = readObjs.size() - 1; i >= 0; i--) {
272: Elem elem = readObjs.get(i);
273: if (!elem.isClosed) {
274: return elem;
275: }
276: }
277: return null;
278: }
279:
280: private Object execute(Elem elem) {
281: if (elem.isExecuted) {
282: return elem.result;
283: }
284:
285: // execute to obtain result
286: try {
287: if (elem.idref != null) {
288: elem.result = idObjMap.get(elem.idref);
289: } else if (elem.isBasicType) {
290: elem.result = executeBasic(elem);
291: } else {
292: elem.result = executeCommon(elem);
293: }
294: } catch (Exception e) {
295: listener.exceptionThrown(e);
296: }
297:
298: // track id
299: if (elem.id != null) {
300: idObjMap.put(elem.id, elem.result);
301: }
302:
303: elem.isExecuted = true;
304: return elem.result;
305: }
306:
307: @SuppressWarnings("nls")
308: private Object executeCommon(Elem elem) throws Exception {
309: // pop args
310: ArrayList<Object> args = new ArrayList<Object>(5);
311: while (readObjs.peek() != elem) {
312: Elem argElem = readObjs.pop();
313: args.add(0, argElem.result);
314: }
315: // decide method name
316: String method = elem.methodName;
317: if (elem.fromProperty) {
318: method = (args.size() == 0 ? "get" : "set")
319: + capitalize(method);
320: }
321: if (elem.fromIndex) {
322: Integer index = Integer.valueOf(method);
323: args.add(0, index);
324: method = args.size() == 1 ? "get" : "set";
325: }
326: if (elem.fromField) {
327: Field f = ((Class) elem.target).getField(method);
328: return (new Expression(f, "get", new Object[] { null }))
329: .getValue();
330: }
331: if (elem.fromOwner) {
332: return owner;
333: }
334:
335: if (elem.target == owner) {
336: if ("getOwner".equals(method)) {
337: return owner;
338: }
339: Class[] c = new Class[args.size()];
340: for (int i = 0; i < args.size(); i++) {
341: c[i] = args.get(i).getClass();
342: }
343: Method m = owner.getClass().getMethod(method, c);
344: return m.invoke(owner, args.toArray());
345: }
346:
347: // execute
348: Expression exp = new Expression(elem.target, method, args
349: .toArray());
350: return exp.getValue();
351: }
352:
353: private String capitalize(String str) {
354: StringBuffer buf = new StringBuffer(str);
355: buf.setCharAt(0, Character.toUpperCase(buf.charAt(0)));
356: return buf.toString();
357: }
358:
359: @SuppressWarnings("nls")
360: private Object executeBasic(Elem elem) throws Exception {
361: String tag = (String) elem.target;
362: String value = elem.methodName;
363:
364: if ("null".equals(tag)) {
365: return null;
366: } else if ("string".equals(tag)) {
367: return value == null ? "" : value;
368: } else if ("class".equals(tag)) {
369: return classForName(value);
370: } else if ("boolean".equals(tag)) {
371: return Boolean.valueOf(value);
372: } else if ("byte".equals(tag)) {
373: return Byte.valueOf(value);
374: } else if ("char".equals(tag)) {
375: return new Character(value.charAt(0));
376: } else if ("double".equals(tag)) {
377: return Double.valueOf(value);
378: } else if ("float".equals(tag)) {
379: return Float.valueOf(value);
380: } else if ("int".equals(tag)) {
381: return Integer.valueOf(value);
382: } else if ("long".equals(tag)) {
383: return Long.valueOf(value);
384: } else if ("short".equals(tag)) {
385: return Short.valueOf(value);
386: } else {
387: throw new Exception("Unknown tag of basic type: " + tag);
388: }
389: }
390:
391: @Override
392: public void error(SAXParseException e) throws SAXException {
393: listener.exceptionThrown(e);
394: }
395:
396: @Override
397: public void fatalError(SAXParseException e) throws SAXException {
398: listener.exceptionThrown(e);
399: }
400:
401: @Override
402: public void warning(SAXParseException e) throws SAXException {
403: listener.exceptionThrown(e);
404: }
405: }
406:
407: private static class Elem {
408: String id;
409:
410: String idref;
411:
412: boolean isExecuted;
413:
414: boolean isExpression;
415:
416: boolean isBasicType;
417:
418: boolean isClosed;
419:
420: Object target;
421:
422: String methodName;
423:
424: boolean fromProperty;
425:
426: boolean fromIndex;
427:
428: boolean fromField;
429:
430: boolean fromOwner;
431:
432: Object result;
433: }
434:
435: private InputStream inputStream;
436:
437: private ExceptionListener listener;
438:
439: private Object owner;
440:
441: private Stack<Elem> readObjs = new Stack<Elem>();
442:
443: private int readObjIndex = 0;
444:
445: /**
446: * Create a decoder to read from specified input stream.
447: *
448: * @param inputStream
449: * an input stream of xml
450: */
451: public XMLDecoder(InputStream inputStream) {
452: this (inputStream, null, null);
453: }
454:
455: /**
456: * Create a decoder to read from specified input stream.
457: *
458: * @param inputStream
459: * an input stream of xml
460: * @param owner
461: * the owner of this decoder
462: */
463: public XMLDecoder(InputStream inputStream, Object owner) {
464: this (inputStream, owner, null);
465: }
466:
467: /**
468: * Create a decoder to read from specified input stream.
469: *
470: * @param inputStream
471: * an input stream of xml
472: * @param owner
473: * the owner of this decoder
474: * @param listener
475: * listen to the exceptions thrown by the decoder
476: */
477: public XMLDecoder(InputStream inputStream, Object owner,
478: ExceptionListener listener) {
479: if (inputStream == null) {
480: throw new IllegalArgumentException(
481: "Input stream cannot be null"); //$NON-NLS-1$
482: }
483: this .inputStream = inputStream;
484: this .owner = owner;
485: this .listener = (listener == null) ? new DefaultExceptionListener()
486: : listener;
487:
488: try {
489: SAXParserFactory.newInstance().newSAXParser().parse(
490: inputStream, new SAXHandler());
491: } catch (Exception e) {
492: this .listener.exceptionThrown(e);
493: }
494: }
495:
496: public XMLDecoder(InputStream inputStream, Object owner,
497: ExceptionListener listener, ClassLoader cl) {
498: this (inputStream, owner, listener);
499: defaultClassLoader = cl;
500: }
501:
502: /**
503: * Close the input stream of xml data.
504: */
505: public void close() {
506: try {
507: inputStream.close();
508: } catch (Exception e) {
509: listener.exceptionThrown(e);
510: }
511: }
512:
513: /**
514: * Returns the exception listener.
515: *
516: * @return the exception listener
517: */
518: public ExceptionListener getExceptionListener() {
519: return listener;
520: }
521:
522: /**
523: * Returns the owner of this decoder.
524: *
525: * @return the owner of this decoder
526: */
527: public Object getOwner() {
528: return owner;
529: }
530:
531: /**
532: * Reads the next object.
533: *
534: * @return the next object
535: * @exception ArrayIndexOutOfBoundsException
536: * if no more objects to read
537: */
538: @SuppressWarnings("nls")
539: public Object readObject() {
540: if (readObjIndex >= readObjs.size()) {
541: throw new ArrayIndexOutOfBoundsException(
542: "no more objects to read");
543: }
544: Elem elem = readObjs.get(readObjIndex);
545: if (!elem.isClosed) {
546: // bad element, error occurred while parsing
547: throw new ArrayIndexOutOfBoundsException(
548: "no more objects to read");
549: }
550: readObjIndex++;
551: return elem.result;
552: }
553:
554: /**
555: * Sets the exception listener.
556: *
557: * @param listener
558: * an exception listener
559: */
560: public void setExceptionListener(ExceptionListener listener) {
561: if (listener != null) {
562: this .listener = listener;
563: }
564: }
565:
566: /**
567: * Sets the owner of this decoder.
568: *
569: * @param owner
570: * the owner of this decoder
571: */
572: public void setOwner(Object owner) {
573: this.owner = owner;
574: }
575: }
|