001: /*
002: * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
003: *
004: * The Apache Software License, Version 1.1
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Caucho Technology (http://www.caucho.com/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
026: * endorse or promote products derived from this software without prior
027: * written permission. For written permission, please contact
028: * info@caucho.com.
029: *
030: * 5. Products derived from this software may not be called "Resin"
031: * nor may "Resin" appear in their names without prior written
032: * permission of Caucho Technology.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045: *
046: * @author Scott Ferguson
047: */
048:
049: package com.caucho.hessian.io;
050:
051: import java.io.IOException;
052: import java.lang.reflect.Constructor;
053: import java.lang.reflect.Method;
054: import java.lang.reflect.Modifier;
055: import java.util.HashMap;
056:
057: /**
058: * Serializing an object for known object types.
059: */
060: public class BeanDeserializer extends AbstractMapDeserializer {
061: private Class _type;
062: private HashMap _methodMap;
063: private Method _readResolve;
064: private Constructor _constructor;
065: private Object[] _constructorArgs;
066:
067: public BeanDeserializer(Class cl) {
068: _type = cl;
069: _methodMap = getMethodMap(cl);
070:
071: _readResolve = getReadResolve(cl);
072:
073: Constructor[] constructors = cl.getConstructors();
074: int bestLength = Integer.MAX_VALUE;
075:
076: for (int i = 0; i < constructors.length; i++) {
077: if (constructors[i].getParameterTypes().length < bestLength) {
078: _constructor = constructors[i];
079: bestLength = _constructor.getParameterTypes().length;
080: }
081: }
082:
083: if (_constructor != null) {
084: _constructor.setAccessible(true);
085: Class[] params = _constructor.getParameterTypes();
086: _constructorArgs = new Object[params.length];
087: for (int i = 0; i < params.length; i++) {
088: _constructorArgs[i] = getParamArg(params[i]);
089: }
090: }
091: }
092:
093: public Class getType() {
094: return _type;
095: }
096:
097: public Object readMap(AbstractHessianInput in) throws IOException {
098: try {
099: Object obj = instantiate();
100:
101: return readMap(in, obj);
102: } catch (IOException e) {
103: throw e;
104: } catch (Exception e) {
105: throw new IOExceptionWrapper(e);
106: }
107: }
108:
109: public Object readMap(AbstractHessianInput in, Object obj)
110: throws IOException {
111: try {
112: int ref = in.addRef(obj);
113:
114: while (!in.isEnd()) {
115: Object key = in.readObject();
116:
117: Method method = (Method) _methodMap.get(key);
118:
119: if (method != null) {
120: Object value = in.readObject(method
121: .getParameterTypes()[0]);
122:
123: method.invoke(obj, new Object[] { value });
124: } else {
125: Object value = in.readObject();
126: }
127: }
128:
129: in.readMapEnd();
130:
131: Object resolve = resolve(obj);
132:
133: if (obj != resolve)
134: in.setRef(ref, resolve);
135:
136: return resolve;
137: } catch (IOException e) {
138: throw e;
139: } catch (Exception e) {
140: throw new IOExceptionWrapper(e);
141: }
142: }
143:
144: private Object resolve(Object obj) {
145: // if there's a readResolve method, call it
146: try {
147: if (_readResolve != null)
148: return _readResolve.invoke(obj, new Object[0]);
149: } catch (Exception e) {
150: }
151:
152: return obj;
153: }
154:
155: protected Object instantiate() throws Exception {
156: return _constructor.newInstance(_constructorArgs);
157: }
158:
159: /**
160: * Returns the readResolve method
161: */
162: protected Method getReadResolve(Class cl) {
163: for (; cl != null; cl = cl.getSuperclass()) {
164: Method[] methods = cl.getDeclaredMethods();
165:
166: for (int i = 0; i < methods.length; i++) {
167: Method method = methods[i];
168:
169: if (method.getName().equals("readResolve")
170: && method.getParameterTypes().length == 0)
171: return method;
172: }
173: }
174:
175: return null;
176: }
177:
178: /**
179: * Creates a map of the classes fields.
180: */
181: protected HashMap getMethodMap(Class cl) {
182: HashMap methodMap = new HashMap();
183:
184: for (; cl != null; cl = cl.getSuperclass()) {
185: Method[] methods = cl.getDeclaredMethods();
186:
187: for (int i = 0; i < methods.length; i++) {
188: Method method = methods[i];
189:
190: if (Modifier.isStatic(method.getModifiers()))
191: continue;
192:
193: String name = method.getName();
194:
195: if (!name.startsWith("set"))
196: continue;
197:
198: Class[] paramTypes = method.getParameterTypes();
199: if (paramTypes.length != 1)
200: continue;
201:
202: if (!method.getReturnType().equals(void.class))
203: continue;
204:
205: if (findGetter(methods, name, paramTypes[0]) == null)
206: continue;
207:
208: // XXX: could parameterize the handler to only deal with public
209: try {
210: method.setAccessible(true);
211: } catch (Throwable e) {
212: e.printStackTrace();
213: }
214:
215: name = name.substring(3);
216:
217: int j = 0;
218: for (; j < name.length()
219: && Character.isUpperCase(name.charAt(j)); j++) {
220: }
221:
222: if (j == 1)
223: name = name.substring(0, j).toLowerCase()
224: + name.substring(j);
225: else if (j > 1)
226: name = name.substring(0, j - 1).toLowerCase()
227: + name.substring(j - 1);
228:
229: methodMap.put(name, method);
230: }
231: }
232:
233: return methodMap;
234: }
235:
236: /**
237: * Finds any matching setter.
238: */
239: private Method findGetter(Method[] methods, String setterName,
240: Class arg) {
241: String getterName = "get" + setterName.substring(3);
242:
243: for (int i = 0; i < methods.length; i++) {
244: Method method = methods[i];
245:
246: if (!method.getName().equals(getterName))
247: continue;
248:
249: if (!method.getReturnType().equals(arg))
250: continue;
251:
252: Class[] params = method.getParameterTypes();
253:
254: if (params.length == 0)
255: return method;
256: }
257:
258: return null;
259: }
260:
261: /**
262: * Creates a map of the classes fields.
263: */
264: protected static Object getParamArg(Class cl) {
265: if (!cl.isPrimitive())
266: return null;
267: else if (boolean.class.equals(cl))
268: return Boolean.FALSE;
269: else if (byte.class.equals(cl))
270: return new Byte((byte) 0);
271: else if (short.class.equals(cl))
272: return new Short((short) 0);
273: else if (char.class.equals(cl))
274: return new Character((char) 0);
275: else if (int.class.equals(cl))
276: return new Integer(0);
277: else if (long.class.equals(cl))
278: return new Long(0);
279: else if (float.class.equals(cl))
280: return new Double(0);
281: else if (double.class.equals(cl))
282: return new Double(0);
283: else
284: throw new UnsupportedOperationException();
285: }
286: }
|