1. /*
  2. * @(#)Encoder.java 1.20 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.io.*;
  9. import java.util.*;
  10. /**
  11. * An <code>Encoder</code> is a class which can be used to create
  12. * files or streams that encode the state of a collection of
  13. * JavaBeans in terms of their public APIs. The <code>Encoder</code>,
  14. * in conjunction with its persistence delegates, is responsible for
  15. * breaking the object graph down into a series of <code>Statements</code>s
  16. * and <code>Expression</code>s which can be used to create it.
  17. * A subclass typically provides a syntax for these expressions
  18. * using some human readable form - like Java source code or XML.
  19. *
  20. * @since 1.4
  21. *
  22. * @version 1.3 11/15/00
  23. * @author Philip Milne
  24. */
  25. public class Encoder {
  26. private Map bindings = new IdentityHashMap();
  27. private ExceptionListener exceptionListener;
  28. boolean executeStatements = true;
  29. private Map attributes;
  30. /**
  31. * Write the specified object to the output stream.
  32. * The serialized form will denote a series of
  33. * expressions, the combined effect of which will create
  34. * an equivalent object when the input stream is read.
  35. * By default, the object is assumed to be a <em>JavaBean</em>
  36. * with a nullary constructor, whose state is defined by
  37. * the matching pairs of "setter" and "getter" methods
  38. * returned by the Introspector.
  39. *
  40. * @param o The object to be written to the stream.
  41. *
  42. * @see XMLDecoder#readObject
  43. */
  44. protected void writeObject(Object o) {
  45. if (o == this) {
  46. return;
  47. }
  48. PersistenceDelegate info = getPersistenceDelegate(o == null ? null : o.getClass());
  49. info.writeObject(o, this);
  50. }
  51. /**
  52. * Sets the exception handler for this stream to <code>exceptionListener</code>.
  53. * The exception handler is notified when this stream catches recoverable
  54. * exceptions.
  55. *
  56. * @param exceptionListener The exception handler for this stream;
  57. * if <code>null</code> the default exception listener will be used.
  58. *
  59. * @see #getExceptionListener
  60. */
  61. public void setExceptionListener(ExceptionListener exceptionListener) {
  62. this.exceptionListener = exceptionListener;
  63. }
  64. /**
  65. * Gets the exception handler for this stream.
  66. *
  67. * @return The exception handler for this stream;
  68. * Will return the default exception listener if this has not explicitly been set.
  69. *
  70. * @see #setExceptionListener
  71. */
  72. public ExceptionListener getExceptionListener() {
  73. return (exceptionListener != null) ? exceptionListener : Statement.defaultExceptionListener;
  74. }
  75. Object getValue(Expression exp) {
  76. try {
  77. return (exp == null) ? null : exp.getValue();
  78. }
  79. catch (Exception e) {
  80. getExceptionListener().exceptionThrown(e);
  81. throw new RuntimeException("failed to evaluate: " + exp.toString());
  82. }
  83. }
  84. /**
  85. * Returns the persistence delegate for the given type.
  86. * The persistence delegate is calculated
  87. * by applying the following of rules in order:
  88. * <ul>
  89. * <li>
  90. * If the type is an array, an internal persistence
  91. * delegate is returned which will instantiate an
  92. * array of the appropriate type and length, initializing
  93. * each of its elements as if they are properties.
  94. * <li>
  95. * If the type is a proxy, an internal persistence
  96. * delegate is returned which will instantiate a
  97. * new proxy instance using the static
  98. * "newProxyInstance" method defined in the
  99. * Proxy class.
  100. * <li>
  101. * If the BeanInfo for this type has a <code>BeanDescriptor</code>
  102. * which defined a "persistenceDelegate" property, this
  103. * value is returned.
  104. * <li>
  105. * In all other cases the default persistence delegate
  106. * is returned. The default persistence delegate assumes
  107. * the type is a <em>JavaBean</em>, implying that it has a nullary constructor
  108. * and that its state may be characterized by the matching pairs
  109. * of "setter" and "getter" methods returned by the Introspector.
  110. * </ul>
  111. *
  112. * @param type The type of the object.
  113. * @return The persistence delegate for this type of object.
  114. *
  115. * @see #setPersistenceDelegate
  116. * @see java.beans.Introspector#getBeanInfo
  117. * @see java.beans.BeanInfo#getBeanDescriptor
  118. */
  119. public PersistenceDelegate getPersistenceDelegate(Class<?> type) {
  120. return MetaData.getPersistenceDelegate(type);
  121. }
  122. /**
  123. * Sets the persistence delegate associated with this <code>type</code> to
  124. * <code>persistenceDelegate</code>.
  125. *
  126. * @param type The class of objects that <code>persistenceDelegate</code> applies to.
  127. * @param persistenceDelegate The persistence delegate for instances of <code>type</code>.
  128. *
  129. * @see #getPersistenceDelegate
  130. * @see java.beans.Introspector#getBeanInfo
  131. * @see java.beans.BeanInfo#getBeanDescriptor
  132. */
  133. public void setPersistenceDelegate(Class<?> type,
  134. PersistenceDelegate persistenceDelegate)
  135. {
  136. MetaData.setPersistenceDelegate(type, persistenceDelegate);
  137. }
  138. /**
  139. * Removes the entry for this instance, returning the old entry.
  140. *
  141. * @param oldInstance The entry that should be removed.
  142. * @return The entry that was removed.
  143. *
  144. * @see #get
  145. */
  146. public Object remove(Object oldInstance) {
  147. Expression exp = (Expression)bindings.remove(oldInstance);
  148. return getValue(exp);
  149. }
  150. /**
  151. * Returns a tentative value for <code>oldInstance</code> in
  152. * the environment created by this stream. A persistence
  153. * delegate can use its <code>mutatesTo</code> method to
  154. * determine whether this value may be initialized to
  155. * form the equivalent object at the output or whether
  156. * a new object must be instantiated afresh. If the
  157. * stream has not yet seen this value, null is returned.
  158. *
  159. * @param oldInstance The instance to be looked up.
  160. * @return The object, null if the object has not been seen before.
  161. */
  162. public Object get(Object oldInstance) {
  163. if (oldInstance == null || oldInstance == this ||
  164. oldInstance.getClass() == String.class) {
  165. return oldInstance;
  166. }
  167. Expression exp = (Expression)bindings.get(oldInstance);
  168. return getValue(exp);
  169. }
  170. private Object writeObject1(Object oldInstance) {
  171. Object o = get(oldInstance);
  172. if (o == null) {
  173. writeObject(oldInstance);
  174. o = get(oldInstance);
  175. }
  176. return o;
  177. }
  178. private Statement cloneStatement(Statement oldExp) {
  179. Object oldTarget = oldExp.getTarget();
  180. Object newTarget = writeObject1(oldTarget);
  181. Object[] oldArgs = oldExp.getArguments();
  182. Object[] newArgs = new Object[oldArgs.length];
  183. for (int i = 0; i < oldArgs.length; i++) {
  184. newArgs[i] = writeObject1(oldArgs[i]);
  185. }
  186. if (oldExp.getClass() == Statement.class) {
  187. return new Statement(newTarget, oldExp.getMethodName(), newArgs);
  188. }
  189. else {
  190. return new Expression(newTarget, oldExp.getMethodName(), newArgs);
  191. }
  192. }
  193. /**
  194. * Writes statement <code>oldStm</code> to the stream.
  195. * The <code>oldStm</code> should be written entirely
  196. * in terms of the callers environment, i.e. the
  197. * target and all arguments should be part of the
  198. * object graph being written. These expressions
  199. * represent a series of "what happened" expressions
  200. * which tell the output stream how to produce an
  201. * object graph like the original.
  202. * <p>
  203. * The implementation of this method will produce
  204. * a second expression to represent the same expression in
  205. * an environment that will exist when the stream is read.
  206. * This is achieved simply by calling <code>writeObject</code>
  207. * on the target and all the arguments and building a new
  208. * expression with the results.
  209. *
  210. * @param oldStm The expression to be written to the stream.
  211. */
  212. public void writeStatement(Statement oldStm) {
  213. // System.out.println("writeStatement: " + oldExp);
  214. Statement newStm = cloneStatement(oldStm);
  215. if (oldStm.getTarget() != this && executeStatements) {
  216. try {
  217. newStm.execute();
  218. } catch (Exception e) {
  219. getExceptionListener().exceptionThrown(new Exception("Encoder: discarding statement "
  220. + newStm, e));
  221. }
  222. }
  223. }
  224. /**
  225. * The implementation first checks to see if an
  226. * expression with this value has already been written.
  227. * If not, the expression is cloned, using
  228. * the same procedure as <code>writeStatement</code>,
  229. * and the value of this expression is reconciled
  230. * with the value of the cloned expression
  231. * by calling <code>writeObject</code>.
  232. *
  233. * @param oldExp The expression to be written to the stream.
  234. */
  235. public void writeExpression(Expression oldExp) {
  236. // System.out.println("Encoder::writeExpression: " + oldExp);
  237. Object oldValue = getValue(oldExp);
  238. if (get(oldValue) != null) {
  239. return;
  240. }
  241. bindings.put(oldValue, (Expression)cloneStatement(oldExp));
  242. writeObject(oldValue);
  243. }
  244. void clear() {
  245. bindings.clear();
  246. }
  247. // Package private method for setting an attributes table for the encoder
  248. void setAttribute(Object key, Object value) {
  249. if (attributes == null) {
  250. attributes = new HashMap();
  251. }
  252. attributes.put(key, value);
  253. }
  254. Object getAttribute(Object key) {
  255. if (attributes == null) {
  256. return null;
  257. }
  258. return attributes.get(key);
  259. }
  260. }