Duplicate :
|
|
Relates :
|
|
Relates :
|
FULL PRODUCT VERSION : java version "1.6.0_20" Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065) Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode) And other versions too ADDITIONAL OS VERSION INFORMATION : All operating systems A DESCRIPTION OF THE PROBLEM : I think there is a bug in the DefaultPersistenceDelegate class in the method: private static boolean definesEquals(Class type) { try { return type == type.getMethod("equals", Object.class).getDeclaringClass(); } catch(NoSuchMethodException e) { return false; } } When used on a class like Rectangle2D.Double it returns false (because the equals() method is defined in the enclosing class). This means that DefaultPersistenceDelegates may NOT get called if a bean property has been initialised to anything other than null and where the property equals method is not declared directly in the class. Without documentation I cannot determine the intent of the existing code for definesEquals() , but if it is to return true if a class has a suitable equals() method then this would seem to work: private static boolean definesEquals(Class type) { try { // return true if type defines a method with signature: boolean equals(Object o) return boolean.class == type.getMethod("equals", Object.class).getReturnType(); } catch (NoSuchMethodException e) { return false; } } STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : Run the test application twice setting THIS_WORKS to true ad then false. EXPECTED VERSUS ACTUAL BEHAVIOR : EXPECTED - THIS_WORKS=true bean as XML is : <?xml version="1.0" encoding="UTF-8"?> <java version="1.6.0_20" class="java.beans.XMLDecoder"> <object class="TestBean"> <void property="bounds"> <object class="java.awt.geom.Rectangle2D$Double"> <double>1.0</double> <double>2.0</double> <double>3.0</double> <double>5.0</double> </object> </void> </object> </java> bean.getBounds() as XML is : <?xml version="1.0" encoding="UTF-8"?> <java version="1.6.0_20" class="java.beans.XMLDecoder"> <object class="java.awt.geom.Rectangle2D$Double"> <double>1.0</double> <double>2.0</double> <double>3.0</double> <double>5.0</double> </object> </java> ACTUAL - THIS_WORKS=false bean as XML is : <?xml version="1.0" encoding="UTF-8"?> <java version="1.6.0_20" class="java.beans.XMLDecoder"> <object class="TestBean"/> </java> bean.getBounds() as XML is : <?xml version="1.0" encoding="UTF-8"?> <java version="1.6.0_20" class="java.beans.XMLDecoder"> <object class="java.awt.geom.Rectangle2D$Double"> <double>1.0</double> <double>2.0</double> <double>3.0</double> <double>5.0</double> </object> </java> REPRODUCIBILITY : This bug can be reproduced always. ---------- BEGIN SOURCE ---------- import java.awt.geom.Rectangle2D; import java.beans.DefaultPersistenceDelegate; import java.beans.XMLEncoder; import java.io.ByteArrayOutputStream; /** * * Bug with XMLEncoder and DefaultPersistenceDelegate * * DefaultPersistenceDelegate is not called when a bean property has been * initialised to non null? * * This TestBean has a bounds property of type Rectangle2D.Double. The * XMLEncoder requires a persistenceDelegate in order to encode * Rectangle2D.Double. * <p> * However, if TestBean is initialised with its bounds property set to anything * other than null, the persistenceDelegate is not invoked for the bounds * property when writeObject is used on the TestBean. The persistenceDelegate * still gets called if you call writeObject on the bounds property rather than * the TestBean. * <p> * * Try running this program changing THIS_WORKS between true and false. * * * @author Michael Ellis. */ public class TestBean { private static boolean THIS_WORKS = false; Rectangle2D.Double bounds; /** * */ public TestBean() { if (THIS_WORKS) { bounds = null; // THIS WORKS } else { bounds = new Rectangle2D.Double(1, 2, 3, 4); // THIS DOESN'T WORK } } public Rectangle2D.Double getBounds() { return bounds; } public void setBounds(Rectangle2D.Double bounds) { this.bounds = bounds; } public static void main(String[] args) { TestBean bean = new TestBean(); bean.setBounds(new Rectangle2D.Double(1, 2, 3, 5)); System.out.printf("THIS_WORKS=%b\n", THIS_WORKS); System.out.printf("bean as XML is :\n%s\n", objectToXML(bean)); System.out.printf("bean.getBounds() as XML is :\n%s\n", objectToXML(bean.getBounds())); } static String objectToXML(Object o) { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); final XMLEncoder encoder = new XMLEncoder(bos); encoder.setPersistenceDelegate(Rectangle2D.Double.class, new DefaultPersistenceDelegate( new String[] { "x", "y", "width", "height" })); encoder.writeObject(o); encoder.close(); return bos.toString(); } } ---------- END SOURCE ---------- CUSTOMER SUBMITTED WORKAROUND : Override the mutatesTo() method when using DefaultPersistenceDelegate, for example: encoder.setPersistenceDelegate(Rectangle2D.Double.class, new DefaultPersistenceDelegate( new String[] { "x", "y", "width", "height" }) { @Override protected boolean mutatesTo(Object oldInstance, Object newInstance) { return (super.mutatesTo(oldInstance, newInstance) && oldInstance.equals(newInstance)); } });
|