All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lite.beans.Encoder Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package lite.beans;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import java.util.Hashtable;
import java.util.IdentityHashMap;

/**
 * The Encoder, together with PersistenceDelegate s, can encode an object into a series of
 * java statements. By executing these statements, a new object can be created and it will has the same state as the
 * original object which has been passed to the encoder. Here "has the same state" means the two objects are
 * indistinguishable from their public API.
 * 

* The Encoder and PersistenceDelegate s do this by creating copies of the input object and * all objects it references. The copy process continues recursively util every object in the object graph has its new * copy and the new version has the same state as the old version. All statements used to create those new objects and * executed on them during the process form the result of encoding. * */ public class Encoder { private static final Hashtable, PersistenceDelegate> delegates = new Hashtable, PersistenceDelegate>(); private static final DefaultPersistenceDelegate defaultPD = new DefaultPersistenceDelegate(); private static final UtilCollectionsPersistenceDelegate utilCollectionsPD = new UtilCollectionsPersistenceDelegate(); private static final ArrayPersistenceDelegate arrayPD = new ArrayPersistenceDelegate(); private static final ProxyPersistenceDelegate proxyPD = new ProxyPersistenceDelegate(); private static final NullPersistenceDelegate nullPD = new NullPersistenceDelegate(); private static final ExceptionListener defaultExListener = new DefaultExceptionListener(); static { PersistenceDelegate ppd = new PrimitiveWrapperPersistenceDelegate(); delegates.put(Boolean.class, ppd); delegates.put(Byte.class, ppd); delegates.put(Character.class, ppd); delegates.put(Double.class, ppd); delegates.put(Float.class, ppd); delegates.put(Integer.class, ppd); delegates.put(Long.class, ppd); delegates.put(Short.class, ppd); delegates.put(Class.class, new ClassPersistenceDelegate()); delegates.put(Field.class, new FieldPersistenceDelegate()); delegates.put(Method.class, new MethodPersistenceDelegate()); delegates.put(String.class, new StringPersistenceDelegate()); delegates.put(Proxy.class, new ProxyPersistenceDelegate()); delegates.put(Date.class, new UtilDatePersistenceDelegate()); PersistenceDelegate pd = new UtilListPersistenceDelegate(); delegates.put(java.util.List.class, pd); delegates.put(java.util.AbstractList.class, pd); pd = new UtilCollectionPersistenceDelegate(); delegates.put(java.util.Collection.class, pd); delegates.put(java.util.AbstractCollection.class, pd); pd = new UtilMapPersistenceDelegate(); delegates.put(java.util.Map.class, pd); delegates.put(java.util.AbstractMap.class, pd); delegates.put(java.util.Hashtable.class, pd); } private ExceptionListener listener = defaultExListener; private IdentityHashMap oldNewMap = new IdentityHashMap(); /** * Construct a new encoder. */ public Encoder() { super(); } /** * Clear all the new objects have been created. */ void clear() { oldNewMap.clear(); } /** * Gets the new copy of the given old object. *

* Strings are special objects which have their new copy by default, so if the old object is a string, it is * returned directly. * * * @param old an old object * @return the new copy of the given old object, or null if there is not one. */ public Object get(Object old) { if (old == null || old.getClass() == String.class) { return old; } return oldNewMap.get(old); } /** * Returns the exception listener of this encoder. *

* An encoder always have a non-null exception listener. A default exception listener is used when the encoder is * created. * * * @return the exception listener of this encoder */ public ExceptionListener getExceptionListener() { return listener; } /** * Sets the exception listener of this encoder. * * @param listener the exception listener to set */ public void setExceptionListener(ExceptionListener listener) { this.listener = listener == null ? defaultExListener : listener; } /** * Returns a PersistenceDelegate for the given class type. *

* The PersistenceDelegate is determined as following: *

    *
  1. If a PersistenceDelegate has been registered by calling * setPersistenceDelegate for the given type, it is returned.
  2. *
  3. If the given type is an array class, a special * PersistenceDelegate for array types is returned.
  4. *
  5. If the given type is a proxy class, a special * PersistenceDelegate for proxy classes is returned.
  6. *
  7. Introspector is used to check the bean descriptor value * "persistenceDelegate". If one is set, it is returned.
  8. *
  9. If none of the above applies, the * DefaultPersistenceDelegate is returned.
  10. *
* * * @param type a class type * @return a PersistenceDelegate for the given class type */ public PersistenceDelegate getPersistenceDelegate(Class type) { if (type == null) { return nullPD; // may be return a special PD? } // registered delegate PersistenceDelegate registeredPD = delegates.get(type); if (registeredPD != null) { return registeredPD; } if (type.getName().startsWith( UtilCollectionsPersistenceDelegate.CLASS_PREFIX)) { return utilCollectionsPD; } if (type.isArray()) { return arrayPD; } if (Proxy.isProxyClass(type)) { return proxyPD; } // check "persistenceDelegate" property try { BeanInfo beanInfo = Introspector.getBeanInfo(type); if (beanInfo != null) { PersistenceDelegate pd = (PersistenceDelegate) beanInfo .getBeanDescriptor().getValue("persistenceDelegate"); //$NON-NLS-1$ if (pd != null) { return pd; } } } catch (Exception e) { // Ignored } // default persistence delegate return defaultPD; } /** * Remove the existing new copy of the given old object. * * @param old an old object * @return the removed new version of the old object, or null if there is not one */ public Object remove(Object old) { return oldNewMap.remove(old); } /** * Register the PersistenceDelegate of the specified type. * * @param type type * @param delegate delegate */ public void setPersistenceDelegate(Class type, PersistenceDelegate delegate) { if (type == null || delegate == null) { throw new NullPointerException(); } delegates.put(type, delegate); } Object expressionValue(Expression exp) { try { return exp == null ? null : exp.getValue(); } catch (IndexOutOfBoundsException e) { return null; } catch (Exception e) { listener.exceptionThrown(new Exception( "failed to excute expression: " + exp, e)); //$NON-NLS-1$ } return null; } private Statement createNewStatement(Statement oldStat) { Object newTarget = createNewObject(oldStat.getTarget()); Object[] oldArgs = oldStat.getArguments(); Object[] newArgs = new Object[oldArgs.length]; for (int index = 0; index < oldArgs.length; index++) { newArgs[index] = createNewObject(oldArgs[index]); } if (oldStat.getClass() == Expression.class) { return new Expression(newTarget, oldStat.getMethodName(), newArgs); } else { return new Statement(newTarget, oldStat.getMethodName(), newArgs); } } private Object createNewObject(Object old) { Object nu = get(old); if (nu == null) { writeObject(old); nu = get(old); } return nu; } /** * Write an expression of old objects. *

* The implementation first check the return value of the expression. If there exists a new version of the object, * simply return. * *

* A new expression is created using the new versions of the target and the arguments. If any of the old objects do * not have its new version yet, * writeObject() is called to create the new version. * *

* The new expression is then executed to obtained a new copy of the old return value. * *

* Call writeObject() with the old return value, so that more statements will be executed on its new * version to change it into the same state as the old value. * * * @param oldExp the expression to write. The target, arguments, and return value of the expression are all old * objects. */ public void writeExpression(Expression oldExp) { if (oldExp == null) { throw new NullPointerException(); } try { // if oldValue exists, no operation Object oldValue = expressionValue(oldExp); if (oldValue == null || get(oldValue) != null) { return; } // copy to newExp Expression newExp = (Expression) createNewStatement(oldExp); // relate oldValue to newValue try { oldNewMap.put(oldValue, newExp.getValue()); } catch (IndexOutOfBoundsException e) { // container does not have any component, set newVal null } // force same state writeObject(oldValue); } catch (Exception e) { listener.exceptionThrown(new Exception( "failed to write expression: " + oldExp, e)); //$NON-NLS-1$ } } /** * Encode the given object into a series of statements and expressions. *

* The implementation simply finds the PersistenceDelegate responsible for the object's class, and * delegate the call to it. * * * @param o the object to encode */ protected void writeObject(Object o) { if (o == null) { return; } getPersistenceDelegate(o.getClass()).writeObject(o, this); } /** * Write a statement of old objects. *

* A new statement is created by using the new versions of the target and arguments. If any of the objects do not * have its new copy yet, * writeObject() is called to create one. * *

* The new statement is then executed to change the state of the new object. * * * @param oldStat a statement of old objects */ public void writeStatement(Statement oldStat) { if (oldStat == null) { throw new NullPointerException(); } Statement newStat = createNewStatement(oldStat); try { // execute newStat newStat.execute(); } catch (Exception e) { listener.exceptionThrown(new Exception( "failed to write statement: " + oldStat, e)); //$NON-NLS-1$ } } private static class DefaultExceptionListener implements ExceptionListener { public void exceptionThrown(Exception exception) { System.err.println("Exception during encoding:" + exception); //$NON-NLS-1$ System.err.println("Continue..."); //$NON-NLS-1$ } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy