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 java.util.*; 28 import java.lang.reflect.*; 29 import java.util.Objects; 30 import sun.reflect.misc.*; 31 32 33 /** 34 * The <code>DefaultPersistenceDelegate</code> is a concrete implementation of 35 * the abstract <code>PersistenceDelegate</code> class and 36 * is the delegate used by default for classes about 37 * which no information is available. The <code>DefaultPersistenceDelegate</code> 38 * provides, version resilient, public API-based persistence for 39 * classes that follow the JavaBeans™ conventions without any class specific 40 * configuration. 41 * <p> 42 * The key assumptions are that the class has a nullary constructor 43 * and that its state is accurately represented by matching pairs 44 * of "setter" and "getter" methods in the order they are returned 45 * by the Introspector. 46 * In addition to providing code-free persistence for JavaBeans, 47 * the <code>DefaultPersistenceDelegate</code> provides a convenient means 48 * to effect persistent storage for classes that have a constructor 49 * that, while not nullary, simply requires some property values 50 * as arguments. 51 * 52 * @see #DefaultPersistenceDelegate(String[]) 53 * @see java.beans.Introspector 54 * 55 * @since 1.4 56 * 57 * @author Philip Milne 58 */ 59 60 public class DefaultPersistenceDelegate extends PersistenceDelegate { 61 private static final String[] EMPTY = {}; 62 private final String[] constructor; 63 private Boolean definesEquals; 64 65 /** 66 * Creates a persistence delegate for a class with a nullary constructor. 67 * 68 * @see #DefaultPersistenceDelegate(java.lang.String[]) 69 */ 70 public DefaultPersistenceDelegate() { 71 this.constructor = EMPTY; 72 } 73 74 /** 75 * Creates a default persistence delegate for a class with a 76 * constructor whose arguments are the values of the property 77 * names as specified by <code>constructorPropertyNames</code>. 78 * The constructor arguments are created by 79 * evaluating the property names in the order they are supplied. 80 * To use this class to specify a single preferred constructor for use 81 * in the serialization of a particular type, we state the 82 * names of the properties that make up the constructor's 83 * arguments. For example, the <code>Font</code> class which 84 * does not define a nullary constructor can be handled 85 * with the following persistence delegate: 86 * 87 * <pre> 88 * new DefaultPersistenceDelegate(new String[]{"name", "style", "size"}); 89 * </pre> 90 * 91 * @param constructorPropertyNames The property names for the arguments of this constructor. 92 * 93 * @see #instantiate 94 */ 95 public DefaultPersistenceDelegate(String[] constructorPropertyNames) { 96 this.constructor = (constructorPropertyNames == null) ? EMPTY : constructorPropertyNames.clone(); 97 } 98 99 private static boolean definesEquals(Class<?> type) { 100 try { 101 return type == type.getMethod("equals", Object.class).getDeclaringClass(); 102 } 103 catch(NoSuchMethodException e) { 104 return false; 105 } 106 } 107 108 private boolean definesEquals(Object instance) { 109 if (definesEquals != null) { 110 return (definesEquals == Boolean.TRUE); 111 } 112 else { 113 boolean result = definesEquals(instance.getClass()); 114 definesEquals = result ? Boolean.TRUE : Boolean.FALSE; 115 return result; 116 } 117 } 118 119 /** 120 * If the number of arguments in the specified constructor is non-zero and 121 * the class of <code>oldInstance</code> explicitly declares an "equals" method 122 * this method returns the value of <code>oldInstance.equals(newInstance)</code>. 123 * Otherwise, this method uses the superclass's definition which returns true if the 124 * classes of the two instances are equal. 125 * 126 * @param oldInstance The instance to be copied. 127 * @param newInstance The instance that is to be modified. 128 * @return True if an equivalent copy of <code>newInstance</code> may be 129 * created by applying a series of mutations to <code>oldInstance</code>. 130 * 131 * @see #DefaultPersistenceDelegate(String[]) 132 */ 133 protected boolean mutatesTo(Object oldInstance, Object newInstance) { 134 // Assume the instance is either mutable or a singleton 135 // if it has a nullary constructor. 136 return (constructor.length == 0) || !definesEquals(oldInstance) ? 137 super.mutatesTo(oldInstance, newInstance) : 138 oldInstance.equals(newInstance); 139 } 140 141 /** 142 * This default implementation of the <code>instantiate</code> method returns 143 * an expression containing the predefined method name "new" which denotes a 144 * call to a constructor with the arguments as specified in 145 * the <code>DefaultPersistenceDelegate</code>'s constructor. 146 * 147 * @param oldInstance The instance to be instantiated. 148 * @param out The code output stream. 149 * @return An expression whose value is <code>oldInstance</code>. 150 * 151 * @throws NullPointerException if {@code out} is {@code null} 152 * and this value is used in the method 153 * 154 * @see #DefaultPersistenceDelegate(String[]) 155 */ 156 protected Expression instantiate(Object oldInstance, Encoder out) { 157 int nArgs = constructor.length; 158 Class<?> type = oldInstance.getClass(); 159 Object[] constructorArgs = new Object[nArgs]; 160 for(int i = 0; i < nArgs; i++) { 161 try { 162 Method method = findMethod(type, this.constructor[i]); 163 constructorArgs[i] = MethodUtil.invoke(method, oldInstance, new Object[0]); 164 } 165 catch (Exception e) { 166 out.getExceptionListener().exceptionThrown(e); 167 } 168 } 169 return new Expression(oldInstance, oldInstance.getClass(), "new", constructorArgs); 331 return; 332 } 333 } 334 335 // Asssume the listeners are in the same order and that there are no gaps. 336 // Eventually, this may need to do true differencing. 337 String addListenerMethodName = d.getAddListenerMethod().getName(); 338 for (int i = newL.length; i < oldL.length; i++) { 339 // System.out.println("Adding listener: " + addListenerMethodName + oldL[i]); 340 invokeStatement(oldInstance, addListenerMethodName, new Object[]{oldL[i]}, out); 341 } 342 343 String removeListenerMethodName = d.getRemoveListenerMethod().getName(); 344 for (int i = oldL.length; i < newL.length; i++) { 345 invokeStatement(oldInstance, removeListenerMethodName, new Object[]{newL[i]}, out); 346 } 347 } 348 } 349 350 /** 351 * This default implementation of the <code>initialize</code> method assumes 352 * all state held in objects of this type is exposed via the 353 * matching pairs of "setter" and "getter" methods in the order 354 * they are returned by the Introspector. If a property descriptor 355 * defines a "transient" attribute with a value equal to 356 * <code>Boolean.TRUE</code> the property is ignored by this 357 * default implementation. Note that this use of the word 358 * "transient" is quite independent of the field modifier 359 * that is used by the <code>ObjectOutputStream</code>. 360 * <p> 361 * For each non-transient property, an expression is created 362 * in which the nullary "getter" method is applied 363 * to the <code>oldInstance</code>. The value of this 364 * expression is the value of the property in the instance that is 365 * being serialized. If the value of this expression 366 * in the cloned environment <code>mutatesTo</code> the 367 * target value, the new value is initialized to make it 368 * equivalent to the old value. In this case, because 369 * the property value has not changed there is no need to 370 * call the corresponding "setter" method and no statement 371 * is emitted. If not however, the expression for this value 372 * is replaced with another expression (normally a constructor) 373 * and the corresponding "setter" method is called to install 374 * the new property value in the object. This scheme removes 375 * default information from the output produced by streams 376 * using this delegate. 377 * <p> 378 * In passing these statements to the output stream, where they 379 * will be executed, side effects are made to the <code>newInstance</code>. 380 * In most cases this allows the problem of properties 381 * whose values depend on each other to actually help the 382 * serialization process by making the number of statements 383 * that need to be written to the output smaller. In general, 384 * the problem of handling interdependent properties is reduced to 385 * that of finding an order for the properties in 386 * a class such that no property value depends on the value of 387 * a subsequent property. 388 * 389 * @param type the type of the instances 390 * @param oldInstance The instance to be copied. 391 * @param newInstance The instance that is to be modified. 392 * @param out The stream to which any initialization statements should be written. 393 * 394 * @throws NullPointerException if {@code out} is {@code null} 395 * 396 * @see java.beans.Introspector#getBeanInfo 397 * @see java.beans.PropertyDescriptor 398 */ 399 protected void initialize(Class<?> type, | 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 java.util.*; 28 import java.lang.reflect.*; 29 import java.util.Objects; 30 import sun.reflect.misc.*; 31 32 33 /** 34 * The {@code DefaultPersistenceDelegate} is a concrete implementation of 35 * the abstract {@code PersistenceDelegate} class and 36 * is the delegate used by default for classes about 37 * which no information is available. The {@code DefaultPersistenceDelegate} 38 * provides, version resilient, public API-based persistence for 39 * classes that follow the JavaBeans™ conventions without any class specific 40 * configuration. 41 * <p> 42 * The key assumptions are that the class has a nullary constructor 43 * and that its state is accurately represented by matching pairs 44 * of "setter" and "getter" methods in the order they are returned 45 * by the Introspector. 46 * In addition to providing code-free persistence for JavaBeans, 47 * the {@code DefaultPersistenceDelegate} provides a convenient means 48 * to effect persistent storage for classes that have a constructor 49 * that, while not nullary, simply requires some property values 50 * as arguments. 51 * 52 * @see #DefaultPersistenceDelegate(String[]) 53 * @see java.beans.Introspector 54 * 55 * @since 1.4 56 * 57 * @author Philip Milne 58 */ 59 60 public class DefaultPersistenceDelegate extends PersistenceDelegate { 61 private static final String[] EMPTY = {}; 62 private final String[] constructor; 63 private Boolean definesEquals; 64 65 /** 66 * Creates a persistence delegate for a class with a nullary constructor. 67 * 68 * @see #DefaultPersistenceDelegate(java.lang.String[]) 69 */ 70 public DefaultPersistenceDelegate() { 71 this.constructor = EMPTY; 72 } 73 74 /** 75 * Creates a default persistence delegate for a class with a 76 * constructor whose arguments are the values of the property 77 * names as specified by {@code constructorPropertyNames}. 78 * The constructor arguments are created by 79 * evaluating the property names in the order they are supplied. 80 * To use this class to specify a single preferred constructor for use 81 * in the serialization of a particular type, we state the 82 * names of the properties that make up the constructor's 83 * arguments. For example, the {@code Font} class which 84 * does not define a nullary constructor can be handled 85 * with the following persistence delegate: 86 * 87 * <pre> 88 * new DefaultPersistenceDelegate(new String[]{"name", "style", "size"}); 89 * </pre> 90 * 91 * @param constructorPropertyNames The property names for the arguments of this constructor. 92 * 93 * @see #instantiate 94 */ 95 public DefaultPersistenceDelegate(String[] constructorPropertyNames) { 96 this.constructor = (constructorPropertyNames == null) ? EMPTY : constructorPropertyNames.clone(); 97 } 98 99 private static boolean definesEquals(Class<?> type) { 100 try { 101 return type == type.getMethod("equals", Object.class).getDeclaringClass(); 102 } 103 catch(NoSuchMethodException e) { 104 return false; 105 } 106 } 107 108 private boolean definesEquals(Object instance) { 109 if (definesEquals != null) { 110 return (definesEquals == Boolean.TRUE); 111 } 112 else { 113 boolean result = definesEquals(instance.getClass()); 114 definesEquals = result ? Boolean.TRUE : Boolean.FALSE; 115 return result; 116 } 117 } 118 119 /** 120 * If the number of arguments in the specified constructor is non-zero and 121 * the class of {@code oldInstance} explicitly declares an "equals" method 122 * this method returns the value of {@code oldInstance.equals(newInstance)}. 123 * Otherwise, this method uses the superclass's definition which returns true if the 124 * classes of the two instances are equal. 125 * 126 * @param oldInstance The instance to be copied. 127 * @param newInstance The instance that is to be modified. 128 * @return True if an equivalent copy of {@code newInstance} may be 129 * created by applying a series of mutations to {@code oldInstance}. 130 * 131 * @see #DefaultPersistenceDelegate(String[]) 132 */ 133 protected boolean mutatesTo(Object oldInstance, Object newInstance) { 134 // Assume the instance is either mutable or a singleton 135 // if it has a nullary constructor. 136 return (constructor.length == 0) || !definesEquals(oldInstance) ? 137 super.mutatesTo(oldInstance, newInstance) : 138 oldInstance.equals(newInstance); 139 } 140 141 /** 142 * This default implementation of the {@code instantiate} method returns 143 * an expression containing the predefined method name "new" which denotes a 144 * call to a constructor with the arguments as specified in 145 * the {@code DefaultPersistenceDelegate}'s constructor. 146 * 147 * @param oldInstance The instance to be instantiated. 148 * @param out The code output stream. 149 * @return An expression whose value is {@code oldInstance}. 150 * 151 * @throws NullPointerException if {@code out} is {@code null} 152 * and this value is used in the method 153 * 154 * @see #DefaultPersistenceDelegate(String[]) 155 */ 156 protected Expression instantiate(Object oldInstance, Encoder out) { 157 int nArgs = constructor.length; 158 Class<?> type = oldInstance.getClass(); 159 Object[] constructorArgs = new Object[nArgs]; 160 for(int i = 0; i < nArgs; i++) { 161 try { 162 Method method = findMethod(type, this.constructor[i]); 163 constructorArgs[i] = MethodUtil.invoke(method, oldInstance, new Object[0]); 164 } 165 catch (Exception e) { 166 out.getExceptionListener().exceptionThrown(e); 167 } 168 } 169 return new Expression(oldInstance, oldInstance.getClass(), "new", constructorArgs); 331 return; 332 } 333 } 334 335 // Asssume the listeners are in the same order and that there are no gaps. 336 // Eventually, this may need to do true differencing. 337 String addListenerMethodName = d.getAddListenerMethod().getName(); 338 for (int i = newL.length; i < oldL.length; i++) { 339 // System.out.println("Adding listener: " + addListenerMethodName + oldL[i]); 340 invokeStatement(oldInstance, addListenerMethodName, new Object[]{oldL[i]}, out); 341 } 342 343 String removeListenerMethodName = d.getRemoveListenerMethod().getName(); 344 for (int i = oldL.length; i < newL.length; i++) { 345 invokeStatement(oldInstance, removeListenerMethodName, new Object[]{newL[i]}, out); 346 } 347 } 348 } 349 350 /** 351 * This default implementation of the {@code initialize} method assumes 352 * all state held in objects of this type is exposed via the 353 * matching pairs of "setter" and "getter" methods in the order 354 * they are returned by the Introspector. If a property descriptor 355 * defines a "transient" attribute with a value equal to 356 * {@code Boolean.TRUE} the property is ignored by this 357 * default implementation. Note that this use of the word 358 * "transient" is quite independent of the field modifier 359 * that is used by the {@code ObjectOutputStream}. 360 * <p> 361 * For each non-transient property, an expression is created 362 * in which the nullary "getter" method is applied 363 * to the {@code oldInstance}. The value of this 364 * expression is the value of the property in the instance that is 365 * being serialized. If the value of this expression 366 * in the cloned environment {@code mutatesTo} the 367 * target value, the new value is initialized to make it 368 * equivalent to the old value. In this case, because 369 * the property value has not changed there is no need to 370 * call the corresponding "setter" method and no statement 371 * is emitted. If not however, the expression for this value 372 * is replaced with another expression (normally a constructor) 373 * and the corresponding "setter" method is called to install 374 * the new property value in the object. This scheme removes 375 * default information from the output produced by streams 376 * using this delegate. 377 * <p> 378 * In passing these statements to the output stream, where they 379 * will be executed, side effects are made to the {@code newInstance}. 380 * In most cases this allows the problem of properties 381 * whose values depend on each other to actually help the 382 * serialization process by making the number of statements 383 * that need to be written to the output smaller. In general, 384 * the problem of handling interdependent properties is reduced to 385 * that of finding an order for the properties in 386 * a class such that no property value depends on the value of 387 * a subsequent property. 388 * 389 * @param type the type of the instances 390 * @param oldInstance The instance to be copied. 391 * @param newInstance The instance that is to be modified. 392 * @param out The stream to which any initialization statements should be written. 393 * 394 * @throws NullPointerException if {@code out} is {@code null} 395 * 396 * @see java.beans.Introspector#getBeanInfo 397 * @see java.beans.PropertyDescriptor 398 */ 399 protected void initialize(Class<?> type, |