1 /*
   2  * Copyright (c) 2000, 2011, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.beans;
  26 
  27 import com.sun.beans.finder.PersistenceDelegateFinder;
  28 
  29 import java.util.HashMap;
  30 import java.util.IdentityHashMap;
  31 import java.util.Map;
  32 
  33 /**
  34  * An <code>Encoder</code> is a class which can be used to create
  35  * files or streams that encode the state of a collection of
  36  * JavaBeans in terms of their public APIs. The <code>Encoder</code>,
  37  * in conjunction with its persistence delegates, is responsible for
  38  * breaking the object graph down into a series of <code>Statements</code>s
  39  * and <code>Expression</code>s which can be used to create it.
  40  * A subclass typically provides a syntax for these expressions
  41  * using some human readable form - like Java source code or XML.
  42  *
  43  * @since 1.4
  44  *
  45  * @author Philip Milne
  46  */
  47 
  48 public class Encoder {
  49     private final PersistenceDelegateFinder finder = new PersistenceDelegateFinder();
  50     private Map<Object, Expression> bindings = new IdentityHashMap<>();
  51     private ExceptionListener exceptionListener;
  52     boolean executeStatements = true;
  53     private Map<Object, Object> 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 by applying
 116      * the following rules in order:
 117      * <ol>
 118      * <li>
 119      * If a persistence delegate is associated with the given type
 120      * by using the {@link #setPersistenceDelegate} method
 121      * it is returned.
 122      * <li>
 123      * A persistence delegate is then looked up by the name
 124      * composed of the fully qualified name of the given type
 125      * and the "PersistenceDelegate" postfix.
 126      * For example, a persistence delegate for the {@code Bean} class
 127      * should be named {@code BeanPersistenceDelegate}
 128      * and located in the same package.
 129      * <pre>
 130      * public class Bean { ... }
 131      * public class BeanPersistenceDelegate { ... }</pre>
 132      * The instance of the {@code BeanPersistenceDelegate} class
 133      * is returned for the {@code Bean} class.
 134      * <li>
 135      * If the type is {@code null},
 136      * a shared internal persistence delegate is returned
 137      * that encodes {@code null} value.
 138      * <li>
 139      * If the type is a {@code enum} declaration,
 140      * a shared internal persistence delegate is returned
 141      * that encodes constants of this enumeration
 142      * by their names.
 143      * <li>
 144      * If the type is a primitive type or the corresponding wrapper,
 145      * a shared internal persistence delegate is returned
 146      * that encodes values of the given type.
 147      * <li>
 148      * If the type is an array,
 149      * a shared internal persistence delegate is returned
 150      * that encodes an array of the appropriate type and length,
 151      * and each of its elements as if they are properties.
 152      * <li>
 153      * If the type is a proxy,
 154      * a shared internal persistence delegate is returned
 155      * that encodes a proxy instance by using
 156      * the {@link java.lang.reflect.Proxy#newProxyInstance} method.
 157      * <li>
 158      * If the {@link BeanInfo} for this type has a {@link BeanDescriptor}
 159      * which defined a "persistenceDelegate" attribute,
 160      * the value of this named attribute is returned.
 161      * <li>
 162      * In all other cases the default persistence delegate is returned.
 163      * The default persistence delegate assumes the type is a <em>JavaBean</em>,
 164      * implying that it has a default constructor and that its state
 165      * may be characterized by the matching pairs of "setter" and "getter"
 166      * methods returned by the {@link Introspector} class.
 167      * The default constructor is the constructor with the greatest number
 168      * of parameters that has the {@link ConstructorProperties} annotation.
 169      * If none of the constructors has the {@code ConstructorProperties} annotation,
 170      * then the nullary constructor (constructor with no parameters) will be used.
 171      * For example, in the following code fragment, the nullary constructor
 172      * for the {@code Foo} class will be used,
 173      * while the two-parameter constructor
 174      * for the {@code Bar} class will be used.
 175      * <pre>
 176      * public class Foo {
 177      *     public Foo() { ... }
 178      *     public Foo(int x) { ... }
 179      * }
 180      * public class Bar {
 181      *     public Bar() { ... }
 182      *     @ConstructorProperties({"x"})
 183      *     public Bar(int x) { ... }
 184      *     @ConstructorProperties({"x", "y"})
 185      *     public Bar(int x, int y) { ... }
 186      * }</pre>
 187      * </ol>
 188      *
 189      * @param type  the class of the objects
 190      * @return the persistence delegate for the given type
 191      *
 192      * @see #setPersistenceDelegate
 193      * @see java.beans.Introspector#getBeanInfo
 194      * @see java.beans.BeanInfo#getBeanDescriptor
 195      */
 196     public PersistenceDelegate getPersistenceDelegate(Class<?> type) {
 197         PersistenceDelegate pd = this.finder.find(type);
 198         if (pd == null) {
 199             pd = MetaData.getPersistenceDelegate(type);
 200             if (pd != null) {
 201                 this.finder.register(type, pd);
 202             }
 203         }
 204         return pd;
 205     }
 206 
 207     /**
 208      * Associates the specified persistence delegate with the given type.
 209      *
 210      * @param type  the class of objects that the specified persistence delegate applies to
 211      * @param delegate  the persistence delegate for instances of the given type
 212      *
 213      * @see #getPersistenceDelegate
 214      * @see java.beans.Introspector#getBeanInfo
 215      * @see java.beans.BeanInfo#getBeanDescriptor
 216      */
 217     public void setPersistenceDelegate(Class<?> type, PersistenceDelegate delegate) {
 218         this.finder.register(type, delegate);
 219     }
 220 
 221     /**
 222      * Removes the entry for this instance, returning the old entry.
 223      *
 224      * @param oldInstance The entry that should be removed.
 225      * @return The entry that was removed.
 226      *
 227      * @see #get
 228      */
 229     public Object remove(Object oldInstance) {
 230         Expression exp = bindings.remove(oldInstance);
 231         return getValue(exp);
 232     }
 233 
 234     /**
 235      * Returns a tentative value for <code>oldInstance</code> in
 236      * the environment created by this stream. A persistence
 237      * delegate can use its <code>mutatesTo</code> method to
 238      * determine whether this value may be initialized to
 239      * form the equivalent object at the output or whether
 240      * a new object must be instantiated afresh. If the
 241      * stream has not yet seen this value, null is returned.
 242      *
 243      * @param  oldInstance The instance to be looked up.
 244      * @return The object, null if the object has not been seen before.
 245      */
 246     public Object get(Object oldInstance) {
 247         if (oldInstance == null || oldInstance == this ||
 248             oldInstance.getClass() == String.class) {
 249             return oldInstance;
 250         }
 251         Expression exp = bindings.get(oldInstance);
 252         return getValue(exp);
 253     }
 254 
 255     private Object writeObject1(Object oldInstance) {
 256         Object o = get(oldInstance);
 257         if (o == null) {
 258             writeObject(oldInstance);
 259             o = get(oldInstance);
 260         }
 261         return o;
 262     }
 263 
 264     private Statement cloneStatement(Statement oldExp) {
 265         Object oldTarget = oldExp.getTarget();
 266         Object newTarget = writeObject1(oldTarget);
 267 
 268         Object[] oldArgs = oldExp.getArguments();
 269         Object[] newArgs = new Object[oldArgs.length];
 270         for (int i = 0; i < oldArgs.length; i++) {
 271             newArgs[i] = writeObject1(oldArgs[i]);
 272         }
 273         Statement newExp = Statement.class.equals(oldExp.getClass())
 274                 ? new Statement(newTarget, oldExp.getMethodName(), newArgs)
 275                 : new Expression(newTarget, oldExp.getMethodName(), newArgs);
 276         newExp.loader = oldExp.loader;
 277         return newExp;
 278     }
 279 
 280     /**
 281      * Writes statement <code>oldStm</code> to the stream.
 282      * The <code>oldStm</code> should be written entirely
 283      * in terms of the callers environment, i.e. the
 284      * target and all arguments should be part of the
 285      * object graph being written. These expressions
 286      * represent a series of "what happened" expressions
 287      * which tell the output stream how to produce an
 288      * object graph like the original.
 289      * <p>
 290      * The implementation of this method will produce
 291      * a second expression to represent the same expression in
 292      * an environment that will exist when the stream is read.
 293      * This is achieved simply by calling <code>writeObject</code>
 294      * on the target and all the arguments and building a new
 295      * expression with the results.
 296      *
 297      * @param oldStm The expression to be written to the stream.
 298      */
 299     public void writeStatement(Statement oldStm) {
 300         // System.out.println("writeStatement: " + oldExp);
 301         Statement newStm = cloneStatement(oldStm);
 302         if (oldStm.getTarget() != this && executeStatements) {
 303             try {
 304                 newStm.execute();
 305             } catch (Exception e) {
 306                 getExceptionListener().exceptionThrown(new Exception("Encoder: discarding statement "
 307                                                                      + newStm, e));
 308             }
 309         }
 310     }
 311 
 312     /**
 313      * The implementation first checks to see if an
 314      * expression with this value has already been written.
 315      * If not, the expression is cloned, using
 316      * the same procedure as <code>writeStatement</code>,
 317      * and the value of this expression is reconciled
 318      * with the value of the cloned expression
 319      * by calling <code>writeObject</code>.
 320      *
 321      * @param oldExp The expression to be written to the stream.
 322      */
 323     public void writeExpression(Expression oldExp) {
 324         // System.out.println("Encoder::writeExpression: " + oldExp);
 325         Object oldValue = getValue(oldExp);
 326         if (get(oldValue) != null) {
 327             return;
 328         }
 329         bindings.put(oldValue, (Expression)cloneStatement(oldExp));
 330         writeObject(oldValue);
 331     }
 332 
 333     void clear() {
 334         bindings.clear();
 335     }
 336 
 337     // Package private method for setting an attributes table for the encoder
 338     void setAttribute(Object key, Object value) {
 339         if (attributes == null) {
 340             attributes = new HashMap<>();
 341         }
 342         attributes.put(key, value);
 343     }
 344 
 345     Object getAttribute(Object key) {
 346         if (attributes == null) {
 347             return null;
 348         }
 349         return attributes.get(key);
 350     }
 351 }