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