001: /*
002: * Copyright (C) 2004, 2005 Joe Walnes.
003: * Copyright (C) 2006, 2007 XStream Committers.
004: * All rights reserved.
005: *
006: * The software in this package is published under the terms of the BSD
007: * style license a copy of which has been included with this distribution in
008: * the LICENSE.txt file.
009: *
010: * Created on 23. August 2004 by Joe Walnes
011: */
012: package com.thoughtworks.xstream.converters.reflection;
013:
014: import com.thoughtworks.xstream.converters.ConversionException;
015:
016: import java.io.ObjectInputStream;
017: import java.io.ObjectOutputStream;
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Map;
023:
024: /**
025: * Convenience wrapper to invoke special serialization methods on objects (and perform reflection caching).
026: *
027: * @author Joe Walnes
028: */
029: public class SerializationMethodInvoker {
030:
031: private Map cache = Collections.synchronizedMap(new HashMap());
032: private static final Object NO_METHOD = new Object();
033: private static final Object[] EMPTY_ARGS = new Object[0];
034:
035: /**
036: * Resolves an object as native serialization does by calling readResolve(), if available.
037: */
038: public Object callReadResolve(Object result) {
039: if (result == null) {
040: return null;
041: } else {
042: Method readResolveMethod = getMethod(result.getClass(),
043: "readResolve", null, true);
044: if (readResolveMethod != null) {
045: try {
046: return readResolveMethod.invoke(result, EMPTY_ARGS);
047: } catch (IllegalAccessException e) {
048: throw new ObjectAccessException("Could not call "
049: + result.getClass().getName()
050: + ".readResolve()", e);
051: } catch (InvocationTargetException e) {
052: throw new ObjectAccessException("Could not call "
053: + result.getClass().getName()
054: + ".readResolve()", e.getTargetException());
055: }
056: } else {
057: return result;
058: }
059: }
060: }
061:
062: public Object callWriteReplace(Object object) {
063: if (object == null) {
064: return null;
065: } else {
066: Method writeReplaceMethod = getMethod(object.getClass(),
067: "writeReplace", null, true);
068: if (writeReplaceMethod != null) {
069: try {
070: Object[] EMPTY_ARGS = new Object[0];
071: return writeReplaceMethod
072: .invoke(object, EMPTY_ARGS);
073: } catch (IllegalAccessException e) {
074: throw new ObjectAccessException("Could not call "
075: + object.getClass().getName()
076: + ".writeReplace()", e);
077: } catch (InvocationTargetException e) {
078: throw new ObjectAccessException("Could not call "
079: + object.getClass().getName()
080: + ".writeReplace()", e.getTargetException());
081: }
082: } else {
083: return object;
084: }
085: }
086: }
087:
088: public boolean supportsReadObject(Class type,
089: boolean includeBaseClasses) {
090: return getMethod(type, "readObject",
091: new Class[] { ObjectInputStream.class },
092: includeBaseClasses) != null;
093: }
094:
095: public void callReadObject(Class type, Object object,
096: ObjectInputStream stream) {
097: try {
098: Method readObjectMethod = getMethod(type, "readObject",
099: new Class[] { ObjectInputStream.class }, false);
100: readObjectMethod.invoke(object, new Object[] { stream });
101: } catch (IllegalAccessException e) {
102: throw new ConversionException("Could not call "
103: + object.getClass().getName() + ".readObject()", e);
104: } catch (InvocationTargetException e) {
105: throw new ConversionException("Could not call "
106: + object.getClass().getName() + ".readObject()", e
107: .getTargetException());
108: }
109: }
110:
111: public boolean supportsWriteObject(Class type,
112: boolean includeBaseClasses) {
113: return getMethod(type, "writeObject",
114: new Class[] { ObjectOutputStream.class },
115: includeBaseClasses) != null;
116: }
117:
118: public void callWriteObject(Class type, Object instance,
119: ObjectOutputStream stream) {
120: try {
121: Method readObjectMethod = getMethod(type, "writeObject",
122: new Class[] { ObjectOutputStream.class }, false);
123: readObjectMethod.invoke(instance, new Object[] { stream });
124: } catch (IllegalAccessException e) {
125: throw new ConversionException("Could not call "
126: + instance.getClass().getName() + ".writeObject()",
127: e);
128: } catch (InvocationTargetException e) {
129: throw new ConversionException("Could not call "
130: + instance.getClass().getName() + ".writeObject()",
131: e.getTargetException());
132: }
133: }
134:
135: private Method getMethod(Class type, String name,
136: Class[] parameterTypes, boolean includeBaseclasses) {
137: Object key = type.getName() + "." + name + "."
138: + includeBaseclasses;
139: if (cache.containsKey(key)) {
140: Object result = cache.get(key);
141: return (Method) (result == NO_METHOD ? null : result);
142: }
143: if (includeBaseclasses) {
144: while (type != null) {
145: try {
146: Method result = type.getDeclaredMethod(name,
147: parameterTypes);
148: result.setAccessible(true);
149: cache.put(key, result);
150: return result;
151: } catch (NoSuchMethodException e) {
152: type = type.getSuperclass();
153: }
154: }
155: cache.put(key, NO_METHOD);
156: return null;
157: } else {
158: try {
159: Method result = type.getDeclaredMethod(name,
160: parameterTypes);
161: result.setAccessible(true);
162: cache.put(key, result);
163: return result;
164: } catch (NoSuchMethodException e) {
165: cache.put(key, NO_METHOD);
166: return null;
167: }
168: }
169: }
170:
171: }
|