< prev index next >

src/java.desktop/share/classes/java/beans/Encoder.java

Print this page




  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) {


 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) {


 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) {


  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} 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},
  37  * in conjunction with its persistence delegates, is responsible for
  38  * breaking the object graph down into a series of {@code Statement}s
  39  * and {@code Expression}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}.
  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} 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) {


 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} in
 236      * the environment created by this stream. A persistence
 237      * delegate can use its {@code mutatesTo} 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) {


 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} to the stream.
 282      * The {@code oldStm} 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}
 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},
 317      * and the value of this expression is reconciled
 318      * with the value of the cloned expression
 319      * by calling {@code writeObject}.
 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) {
< prev index next >