Save This Page
Home » openjdk-7 » java » beans » [javadoc | source]
    1   /*
    2    * Copyright 2000-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   package java.beans;
   26   
   27   import java.util.Collections;
   28   import java.util.HashMap;
   29   import java.util.IdentityHashMap;
   30   import java.util.Map;
   31   
   32   /**
   33    * An <code>Encoder</code> is a class which can be used to create
   34    * files or streams that encode the state of a collection of
   35    * JavaBeans in terms of their public APIs. The <code>Encoder</code>,
   36    * in conjunction with its persistence delegates, is responsible for
   37    * breaking the object graph down into a series of <code>Statements</code>s
   38    * and <code>Expression</code>s which can be used to create it.
   39    * A subclass typically provides a syntax for these expressions
   40    * using some human readable form - like Java source code or XML.
   41    *
   42    * @since 1.4
   43    *
   44    * @author Philip Milne
   45    */
   46   
   47   public class Encoder {
   48       private final Map<Class<?>, PersistenceDelegate> delegates
   49               = Collections.synchronizedMap(new HashMap<Class<?>, PersistenceDelegate>());
   50       private Map bindings = new IdentityHashMap();
   51       private ExceptionListener exceptionListener;
   52       boolean executeStatements = true;
   53       private Map attributes;
   54   
   55       /**
   56        * Write the specified object to the output stream.
   57        * The serialized form will denote a series of
   58        * expressions, the combined effect of which will create
   59        * an equivalent object when the input stream is read.
   60        * By default, the object is assumed to be a <em>JavaBean</em>
   61        * with a nullary constructor, whose state is defined by
   62        * the matching pairs of "setter" and "getter" methods
   63        * returned by the Introspector.
   64        *
   65        * @param o The object to be written to the stream.
   66        *
   67        * @see XMLDecoder#readObject
   68        */
   69       protected void writeObject(Object o) {
   70           if (o == this) {
   71               return;
   72           }
   73           PersistenceDelegate info = getPersistenceDelegate(o == null ? null : o.getClass());
   74           info.writeObject(o, this);
   75       }
   76   
   77       /**
   78        * Sets the exception handler for this stream to <code>exceptionListener</code>.
   79        * The exception handler is notified when this stream catches recoverable
   80        * exceptions.
   81        *
   82        * @param exceptionListener The exception handler for this stream;
   83        *       if <code>null</code> the default exception listener will be used.
   84        *
   85        * @see #getExceptionListener
   86        */
   87       public void setExceptionListener(ExceptionListener exceptionListener) {
   88           this.exceptionListener = exceptionListener;
   89       }
   90   
   91       /**
   92        * Gets the exception handler for this stream.
   93        *
   94        * @return The exception handler for this stream;
   95        *    Will return the default exception listener if this has not explicitly been set.
   96        *
   97        * @see #setExceptionListener
   98        */
   99       public ExceptionListener getExceptionListener() {
  100           return (exceptionListener != null) ? exceptionListener : Statement.defaultExceptionListener;
  101       }
  102   
  103       Object getValue(Expression exp) {
  104           try {
  105               return (exp == null) ? null : exp.getValue();
  106           }
  107           catch (Exception e) {
  108               getExceptionListener().exceptionThrown(e);
  109               throw new RuntimeException("failed to evaluate: " + exp.toString());
  110           }
  111       }
  112   
  113       /**
  114        * Returns the persistence delegate for the given type.
  115        * The persistence delegate is calculated
  116        * by applying the following of rules in order:
  117        * <ul>
  118        * <li>
  119        * If the type is an array, an internal persistence
  120        * delegate is returned which will instantiate an
  121        * array of the appropriate type and length, initializing
  122        * each of its elements as if they are properties.
  123        * <li>
  124        * If the type is a proxy, an internal persistence
  125        * delegate is returned which will instantiate a
  126        * new proxy instance using the static
  127        * "newProxyInstance" method defined in the
  128        * Proxy class.
  129        * <li>
  130        * If the BeanInfo for this type has a <code>BeanDescriptor</code>
  131        * which defined a "persistenceDelegate" property, this
  132        * value is returned.
  133        * <li>
  134        * In all other cases the default persistence delegate
  135        * is returned. The default persistence delegate assumes
  136        * the type is a <em>JavaBean</em>, implying that it has a default constructor
  137        * and that its state may be characterized by the matching pairs
  138        * of "setter" and "getter" methods returned by the Introspector.
  139        * The default constructor is the constructor with the greatest number
  140        * of parameters that has the {@link ConstructorProperties} annotation.
  141        * If none of the constructors have the {@code ConstructorProperties} annotation,
  142        * then the nullary constructor (constructor with no parameters) will be used.
  143        * For example, in the following the nullary constructor
  144        * for {@code Foo} will be used, while the two parameter constructor
  145        * for {@code Bar} will be used.
  146        * <code>
  147        *   public class Foo {
  148        *     public Foo() { ... }
  149        *     public Foo(int x) { ... }
  150        *   }
  151        *   public class Bar {
  152        *     public Bar() { ... }
  153        *     &#64;ConstructorProperties({"x"})
  154        *     public Bar(int x) { ... }
  155        *     &#64;ConstructorProperties({"x", "y"})
  156        *     public Bar(int x, int y) { ... }
  157        *   }
  158        * </code>
  159        * </ul>
  160        *
  161        * @param  type The type of the object.
  162        * @return The persistence delegate for this type of object.
  163        *
  164        * @see #setPersistenceDelegate
  165        * @see java.beans.Introspector#getBeanInfo
  166        * @see java.beans.BeanInfo#getBeanDescriptor
  167        */
  168       public PersistenceDelegate getPersistenceDelegate(Class<?> type) {
  169           PersistenceDelegate pd = this.delegates.get(type);
  170           return (pd != null) ? pd : MetaData.getPersistenceDelegate(type);
  171       }
  172   
  173       /**
  174        * Sets the persistence delegate associated with this <code>type</code> to
  175        * <code>persistenceDelegate</code>.
  176        *
  177        * @param  type The class of objects that <code>persistenceDelegate</code> applies to.
  178        * @param  persistenceDelegate The persistence delegate for instances of <code>type</code>.
  179        *
  180        * @see #getPersistenceDelegate
  181        * @see java.beans.Introspector#getBeanInfo
  182        * @see java.beans.BeanInfo#getBeanDescriptor
  183        */
  184       public void setPersistenceDelegate(Class<?> type,
  185                                          PersistenceDelegate persistenceDelegate)
  186       {
  187           if (persistenceDelegate != null) {
  188               this.delegates.put(type, persistenceDelegate);
  189           } else {
  190               this.delegates.remove(type);
  191           }
  192       }
  193   
  194       /**
  195        * Removes the entry for this instance, returning the old entry.
  196        *
  197        * @param oldInstance The entry that should be removed.
  198        * @return The entry that was removed.
  199        *
  200        * @see #get
  201        */
  202       public Object remove(Object oldInstance) {
  203           Expression exp = (Expression)bindings.remove(oldInstance);
  204           return getValue(exp);
  205       }
  206   
  207       /**
  208        * Returns a tentative value for <code>oldInstance</code> in
  209        * the environment created by this stream. A persistence
  210        * delegate can use its <code>mutatesTo</code> method to
  211        * determine whether this value may be initialized to
  212        * form the equivalent object at the output or whether
  213        * a new object must be instantiated afresh. If the
  214        * stream has not yet seen this value, null is returned.
  215        *
  216        * @param  oldInstance The instance to be looked up.
  217        * @return The object, null if the object has not been seen before.
  218        */
  219       public Object get(Object oldInstance) {
  220           if (oldInstance == null || oldInstance == this ||
  221               oldInstance.getClass() == String.class) {
  222               return oldInstance;
  223           }
  224           Expression exp = (Expression)bindings.get(oldInstance);
  225           return getValue(exp);
  226       }
  227   
  228       private Object writeObject1(Object oldInstance) {
  229           Object o = get(oldInstance);
  230           if (o == null) {
  231               writeObject(oldInstance);
  232               o = get(oldInstance);
  233           }
  234           return o;
  235       }
  236   
  237       private Statement cloneStatement(Statement oldExp) {
  238           Object oldTarget = oldExp.getTarget();
  239           Object newTarget = writeObject1(oldTarget);
  240   
  241           Object[] oldArgs = oldExp.getArguments();
  242           Object[] newArgs = new Object[oldArgs.length];
  243           for (int i = 0; i < oldArgs.length; i++) {
  244               newArgs[i] = writeObject1(oldArgs[i]);
  245           }
  246           if (oldExp.getClass() == Statement.class) {
  247               return new Statement(newTarget, oldExp.getMethodName(), newArgs);
  248           }
  249           else {
  250               return new Expression(newTarget, oldExp.getMethodName(), newArgs);
  251           }
  252       }
  253   
  254       /**
  255        * Writes statement <code>oldStm</code> to the stream.
  256        * The <code>oldStm</code> should be written entirely
  257        * in terms of the callers environment, i.e. the
  258        * target and all arguments should be part of the
  259        * object graph being written. These expressions
  260        * represent a series of "what happened" expressions
  261        * which tell the output stream how to produce an
  262        * object graph like the original.
  263        * <p>
  264        * The implementation of this method will produce
  265        * a second expression to represent the same expression in
  266        * an environment that will exist when the stream is read.
  267        * This is achieved simply by calling <code>writeObject</code>
  268        * on the target and all the arguments and building a new
  269        * expression with the results.
  270        *
  271        * @param oldStm The expression to be written to the stream.
  272        */
  273       public void writeStatement(Statement oldStm) {
  274           // System.out.println("writeStatement: " + oldExp);
  275           Statement newStm = cloneStatement(oldStm);
  276           if (oldStm.getTarget() != this && executeStatements) {
  277               try {
  278                   newStm.execute();
  279               } catch (Exception e) {
  280                   getExceptionListener().exceptionThrown(new Exception("Encoder: discarding statement "
  281                                                                        + newStm, e));
  282               }
  283           }
  284       }
  285   
  286       /**
  287        * The implementation first checks to see if an
  288        * expression with this value has already been written.
  289        * If not, the expression is cloned, using
  290        * the same procedure as <code>writeStatement</code>,
  291        * and the value of this expression is reconciled
  292        * with the value of the cloned expression
  293        * by calling <code>writeObject</code>.
  294        *
  295        * @param oldExp The expression to be written to the stream.
  296        */
  297       public void writeExpression(Expression oldExp) {
  298           // System.out.println("Encoder::writeExpression: " + oldExp);
  299           Object oldValue = getValue(oldExp);
  300           if (get(oldValue) != null) {
  301               return;
  302           }
  303           bindings.put(oldValue, (Expression)cloneStatement(oldExp));
  304           writeObject(oldValue);
  305       }
  306   
  307       void clear() {
  308           bindings.clear();
  309       }
  310   
  311       // Package private method for setting an attributes table for the encoder
  312       void setAttribute(Object key, Object value) {
  313           if (attributes == null) {
  314               attributes = new HashMap();
  315           }
  316           attributes.put(key, value);
  317       }
  318   
  319       Object getAttribute(Object key) {
  320           if (attributes == null) {
  321               return null;
  322           }
  323           return attributes.get(key);
  324       }
  325   }

Save This Page
Home » openjdk-7 » java » beans » [javadoc | source]