1 /*
   2  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  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.PrimitiveWrapperMap;
  28 
  29 import java.awt.AWTKeyStroke;
  30 import java.awt.BorderLayout;
  31 import java.awt.Dimension;
  32 import java.awt.Color;
  33 import java.awt.Font;
  34 import java.awt.GridBagConstraints;
  35 import java.awt.Insets;
  36 import java.awt.Point;
  37 import java.awt.Rectangle;
  38 import java.awt.event.KeyEvent;
  39 import java.awt.font.TextAttribute;
  40 
  41 import java.lang.reflect.Array;
  42 import java.lang.reflect.Constructor;
  43 import java.lang.reflect.Field;
  44 import java.lang.reflect.Method;
  45 import java.lang.reflect.InvocationTargetException;
  46 
  47 import java.security.AccessController;
  48 import java.security.PrivilegedAction;
  49 
  50 import java.util.*;
  51 
  52 import javax.swing.Box;
  53 import javax.swing.JLayeredPane;
  54 import javax.swing.border.MatteBorder;
  55 import javax.swing.plaf.ColorUIResource;
  56 
  57 import sun.swing.PrintColorUIResource;
  58 
  59 import java.util.Objects;
  60 
  61 /*
  62  * Like the <code>Intropector</code>, the <code>MetaData</code> class
  63  * contains <em>meta</em> objects that describe the way
  64  * classes should express their state in terms of their
  65  * own public APIs.
  66  *
  67  * @see java.beans.Intropector
  68  *
  69  * @author Philip Milne
  70  * @author Steve Langley
  71  */
  72 
  73 class NullPersistenceDelegate extends PersistenceDelegate {
  74     // Note this will be called by all classes when they reach the
  75     // top of their superclass chain.
  76     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
  77     }
  78     protected Expression instantiate(Object oldInstance, Encoder out) { return null; }
  79 
  80     public void writeObject(Object oldInstance, Encoder out) {
  81     // System.out.println("NullPersistenceDelegate:writeObject " + oldInstance);
  82     }
  83 }
  84 
  85 /**
  86  * The persistence delegate for <CODE>enum</CODE> classes.
  87  *
  88  * @author Sergey A. Malenkov
  89  */
  90 class EnumPersistenceDelegate extends PersistenceDelegate {
  91     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
  92         return oldInstance == newInstance;
  93     }
  94 
  95     protected Expression instantiate(Object oldInstance, Encoder out) {
  96         Enum e = (Enum) oldInstance;
  97         return new Expression(e, Enum.class, "valueOf", new Object[]{e.getDeclaringClass(), e.name()});
  98     }
  99 }
 100 
 101 class PrimitivePersistenceDelegate extends PersistenceDelegate {
 102     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 103         return oldInstance.equals(newInstance);
 104     }
 105 
 106     protected Expression instantiate(Object oldInstance, Encoder out) {
 107         return new Expression(oldInstance, oldInstance.getClass(),
 108                   "new", new Object[]{oldInstance.toString()});
 109     }
 110 }
 111 
 112 class ArrayPersistenceDelegate extends PersistenceDelegate {
 113     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 114         return (newInstance != null &&
 115                 oldInstance.getClass() == newInstance.getClass() && // Also ensures the subtype is correct.
 116                 Array.getLength(oldInstance) == Array.getLength(newInstance));
 117         }
 118 
 119     protected Expression instantiate(Object oldInstance, Encoder out) {
 120         // System.out.println("instantiate: " + type + " " + oldInstance);
 121         Class oldClass = oldInstance.getClass();
 122         return new Expression(oldInstance, Array.class, "newInstance",
 123                    new Object[]{oldClass.getComponentType(),
 124                                 new Integer(Array.getLength(oldInstance))});
 125         }
 126 
 127     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 128         int n = Array.getLength(oldInstance);
 129         for (int i = 0; i < n; i++) {
 130             Object index = new Integer(i);
 131             // Expression oldGetExp = new Expression(Array.class, "get", new Object[]{oldInstance, index});
 132             // Expression newGetExp = new Expression(Array.class, "get", new Object[]{newInstance, index});
 133             Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index});
 134             Expression newGetExp = new Expression(newInstance, "get", new Object[]{index});
 135             try {
 136                 Object oldValue = oldGetExp.getValue();
 137                 Object newValue = newGetExp.getValue();
 138                 out.writeExpression(oldGetExp);
 139                 if (!Objects.equals(newValue, out.get(oldValue))) {
 140                     // System.out.println("Not equal: " + newGetExp + " != " + actualGetExp);
 141                     // invokeStatement(Array.class, "set", new Object[]{oldInstance, index, oldValue}, out);
 142                     DefaultPersistenceDelegate.invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out);
 143                 }
 144             }
 145             catch (Exception e) {
 146                 // System.err.println("Warning:: failed to write: " + oldGetExp);
 147                 out.getExceptionListener().exceptionThrown(e);
 148             }
 149         }
 150     }
 151 }
 152 
 153 class ProxyPersistenceDelegate extends PersistenceDelegate {
 154     protected Expression instantiate(Object oldInstance, Encoder out) {
 155         Class type = oldInstance.getClass();
 156         java.lang.reflect.Proxy p = (java.lang.reflect.Proxy)oldInstance;
 157         // This unappealing hack is not required but makes the
 158         // representation of EventHandlers much more concise.
 159         java.lang.reflect.InvocationHandler ih = java.lang.reflect.Proxy.getInvocationHandler(p);
 160         if (ih instanceof EventHandler) {
 161             EventHandler eh = (EventHandler)ih;
 162             Vector args = new Vector();
 163             args.add(type.getInterfaces()[0]);
 164             args.add(eh.getTarget());
 165             args.add(eh.getAction());
 166             if (eh.getEventPropertyName() != null) {
 167                 args.add(eh.getEventPropertyName());
 168             }
 169             if (eh.getListenerMethodName() != null) {
 170                 args.setSize(4);
 171                 args.add(eh.getListenerMethodName());
 172             }
 173             return new Expression(oldInstance,
 174                                   EventHandler.class,
 175                                   "create",
 176                                   args.toArray());
 177         }
 178         return new Expression(oldInstance,
 179                               java.lang.reflect.Proxy.class,
 180                               "newProxyInstance",
 181                               new Object[]{type.getClassLoader(),
 182                                            type.getInterfaces(),
 183                                            ih});
 184     }
 185 }
 186 
 187 // Strings
 188 class java_lang_String_PersistenceDelegate extends PersistenceDelegate {
 189     protected Expression instantiate(Object oldInstance, Encoder out) { return null; }
 190 
 191     public void writeObject(Object oldInstance, Encoder out) {
 192         // System.out.println("NullPersistenceDelegate:writeObject " + oldInstance);
 193     }
 194 }
 195 
 196 // Classes
 197 class java_lang_Class_PersistenceDelegate extends PersistenceDelegate {
 198     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 199         return oldInstance.equals(newInstance);
 200     }
 201 
 202     protected Expression instantiate(Object oldInstance, Encoder out) {
 203         Class c = (Class)oldInstance;
 204         // As of 1.3 it is not possible to call Class.forName("int"),
 205         // so we have to generate different code for primitive types.
 206         // This is needed for arrays whose subtype may be primitive.
 207         if (c.isPrimitive()) {
 208             Field field = null;
 209             try {
 210                 field = PrimitiveWrapperMap.getType(c.getName()).getDeclaredField("TYPE");
 211             } catch (NoSuchFieldException ex) {
 212                 System.err.println("Unknown primitive type: " + c);
 213             }
 214             return new Expression(oldInstance, field, "get", new Object[]{null});
 215         }
 216         else if (oldInstance == String.class) {
 217             return new Expression(oldInstance, "", "getClass", new Object[]{});
 218         }
 219         else if (oldInstance == Class.class) {
 220             return new Expression(oldInstance, String.class, "getClass", new Object[]{});
 221         }
 222         else {
 223             Expression newInstance = new Expression(oldInstance, Class.class, "forName", new Object[] { c.getName() });
 224             newInstance.loader = c.getClassLoader();
 225             return newInstance;
 226         }
 227     }
 228 }
 229 
 230 // Fields
 231 class java_lang_reflect_Field_PersistenceDelegate extends PersistenceDelegate {
 232     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 233         return oldInstance.equals(newInstance);
 234     }
 235 
 236     protected Expression instantiate(Object oldInstance, Encoder out) {
 237         Field f = (Field)oldInstance;
 238         return new Expression(oldInstance,
 239                 f.getDeclaringClass(),
 240                 "getField",
 241                 new Object[]{f.getName()});
 242     }
 243 }
 244 
 245 // Methods
 246 class java_lang_reflect_Method_PersistenceDelegate extends PersistenceDelegate {
 247     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 248         return oldInstance.equals(newInstance);
 249     }
 250 
 251     protected Expression instantiate(Object oldInstance, Encoder out) {
 252         Method m = (Method)oldInstance;
 253         return new Expression(oldInstance,
 254                 m.getDeclaringClass(),
 255                 "getMethod",
 256                 new Object[]{m.getName(), m.getParameterTypes()});
 257     }
 258 }
 259 
 260 // Dates
 261 
 262 /**
 263  * The persistence delegate for <CODE>java.util.Date</CODE> classes.
 264  * Do not extend DefaultPersistenceDelegate to improve performance and
 265  * to avoid problems with <CODE>java.sql.Date</CODE>,
 266  * <CODE>java.sql.Time</CODE> and <CODE>java.sql.Timestamp</CODE>.
 267  *
 268  * @author Sergey A. Malenkov
 269  */
 270 class java_util_Date_PersistenceDelegate extends PersistenceDelegate {
 271     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 272         if (!super.mutatesTo(oldInstance, newInstance)) {
 273             return false;
 274         }
 275         Date oldDate = (Date)oldInstance;
 276         Date newDate = (Date)newInstance;
 277 
 278         return oldDate.getTime() == newDate.getTime();
 279     }
 280 
 281     protected Expression instantiate(Object oldInstance, Encoder out) {
 282         Date date = (Date)oldInstance;
 283         return new Expression(date, date.getClass(), "new", new Object[] {date.getTime()});
 284     }
 285 }
 286 
 287 /**
 288  * The persistence delegate for <CODE>java.sql.Timestamp</CODE> classes.
 289  * It supports nanoseconds.
 290  *
 291  * @author Sergey A. Malenkov
 292  */
 293 final class java_sql_Timestamp_PersistenceDelegate extends java_util_Date_PersistenceDelegate {
 294     private static final Method getNanosMethod = getNanosMethod();
 295 
 296     private static Method getNanosMethod() {
 297         try {
 298             Class<?> c = Class.forName("java.sql.Timestamp", true, null);
 299             return c.getMethod("getNanos");
 300         } catch (ClassNotFoundException e) {
 301             return null;
 302         } catch (NoSuchMethodException e) {
 303             throw new AssertionError(e);
 304         }
 305     }
 306 
 307     /**
 308      * Invoke Timstamp getNanos.
 309      */
 310     private static int getNanos(Object obj) {
 311         if (getNanosMethod == null)
 312             throw new AssertionError("Should not get here");
 313         try {
 314             return (Integer)getNanosMethod.invoke(obj);
 315         } catch (InvocationTargetException e) {
 316             Throwable cause = e.getCause();
 317             if (cause instanceof RuntimeException)
 318                 throw (RuntimeException)cause;
 319             if (cause instanceof Error)
 320                 throw (Error)cause;
 321             throw new AssertionError(e);
 322         } catch (IllegalAccessException iae) {
 323             throw new AssertionError(iae);
 324         }
 325     }
 326 
 327     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 328         // assumes oldInstance and newInstance are Timestamps
 329         int nanos = getNanos(oldInstance);
 330         if (nanos != getNanos(newInstance)) {
 331             out.writeStatement(new Statement(oldInstance, "setNanos", new Object[] {nanos}));
 332         }
 333     }
 334 }
 335 
 336 // Collections
 337 
 338 /*
 339 The Hashtable and AbstractMap classes have no common ancestor yet may
 340 be handled with a single persistence delegate: one which uses the methods
 341 of the Map insterface exclusively. Attatching the persistence delegates
 342 to the interfaces themselves is fraught however since, in the case of
 343 the Map, both the AbstractMap and HashMap classes are declared to
 344 implement the Map interface, leaving the obvious implementation prone
 345 to repeating their initialization. These issues and questions around
 346 the ordering of delegates attached to interfaces have lead us to
 347 ignore any delegates attached to interfaces and force all persistence
 348 delegates to be registered with concrete classes.
 349 */
 350 
 351 /**
 352  * The base class for persistence delegates for inner classes
 353  * that can be created using {@link Collections}.
 354  *
 355  * @author Sergey A. Malenkov
 356  */
 357 abstract class java_util_Collections extends PersistenceDelegate {
 358     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 359         if (!super.mutatesTo(oldInstance, newInstance)) {
 360             return false;
 361         }
 362         if ((oldInstance instanceof List) || (oldInstance instanceof Set) || (oldInstance instanceof Map)) {
 363             return oldInstance.equals(newInstance);
 364         }
 365         Collection oldC = (Collection) oldInstance;
 366         Collection newC = (Collection) newInstance;
 367         return (oldC.size() == newC.size()) && oldC.containsAll(newC);
 368     }
 369 
 370     static final class EmptyList_PersistenceDelegate extends java_util_Collections {
 371         protected Expression instantiate(Object oldInstance, Encoder out) {
 372             return new Expression(oldInstance, Collections.class, "emptyList", null);
 373         }
 374     }
 375 
 376     static final class EmptySet_PersistenceDelegate extends java_util_Collections {
 377         protected Expression instantiate(Object oldInstance, Encoder out) {
 378             return new Expression(oldInstance, Collections.class, "emptySet", null);
 379         }
 380     }
 381 
 382     static final class EmptyMap_PersistenceDelegate extends java_util_Collections {
 383         protected Expression instantiate(Object oldInstance, Encoder out) {
 384             return new Expression(oldInstance, Collections.class, "emptyMap", null);
 385         }
 386     }
 387 
 388     static final class SingletonList_PersistenceDelegate extends java_util_Collections {
 389         protected Expression instantiate(Object oldInstance, Encoder out) {
 390             List list = (List) oldInstance;
 391             return new Expression(oldInstance, Collections.class, "singletonList", new Object[]{list.get(0)});
 392         }
 393     }
 394 
 395     static final class SingletonSet_PersistenceDelegate extends java_util_Collections {
 396         protected Expression instantiate(Object oldInstance, Encoder out) {
 397             Set set = (Set) oldInstance;
 398             return new Expression(oldInstance, Collections.class, "singleton", new Object[]{set.iterator().next()});
 399         }
 400     }
 401 
 402     static final class SingletonMap_PersistenceDelegate extends java_util_Collections {
 403         protected Expression instantiate(Object oldInstance, Encoder out) {
 404             Map map = (Map) oldInstance;
 405             Object key = map.keySet().iterator().next();
 406             return new Expression(oldInstance, Collections.class, "singletonMap", new Object[]{key, map.get(key)});
 407         }
 408     }
 409 
 410     static final class UnmodifiableCollection_PersistenceDelegate extends java_util_Collections {
 411         protected Expression instantiate(Object oldInstance, Encoder out) {
 412             List list = new ArrayList((Collection) oldInstance);
 413             return new Expression(oldInstance, Collections.class, "unmodifiableCollection", new Object[]{list});
 414         }
 415     }
 416 
 417     static final class UnmodifiableList_PersistenceDelegate extends java_util_Collections {
 418         protected Expression instantiate(Object oldInstance, Encoder out) {
 419             List list = new LinkedList((Collection) oldInstance);
 420             return new Expression(oldInstance, Collections.class, "unmodifiableList", new Object[]{list});
 421         }
 422     }
 423 
 424     static final class UnmodifiableRandomAccessList_PersistenceDelegate extends java_util_Collections {
 425         protected Expression instantiate(Object oldInstance, Encoder out) {
 426             List list = new ArrayList((Collection) oldInstance);
 427             return new Expression(oldInstance, Collections.class, "unmodifiableList", new Object[]{list});
 428         }
 429     }
 430 
 431     static final class UnmodifiableSet_PersistenceDelegate extends java_util_Collections {
 432         protected Expression instantiate(Object oldInstance, Encoder out) {
 433             Set set = new HashSet((Set) oldInstance);
 434             return new Expression(oldInstance, Collections.class, "unmodifiableSet", new Object[]{set});
 435         }
 436     }
 437 
 438     static final class UnmodifiableSortedSet_PersistenceDelegate extends java_util_Collections {
 439         protected Expression instantiate(Object oldInstance, Encoder out) {
 440             SortedSet set = new TreeSet((SortedSet) oldInstance);
 441             return new Expression(oldInstance, Collections.class, "unmodifiableSortedSet", new Object[]{set});
 442         }
 443     }
 444 
 445     static final class UnmodifiableMap_PersistenceDelegate extends java_util_Collections {
 446         protected Expression instantiate(Object oldInstance, Encoder out) {
 447             Map map = new HashMap((Map) oldInstance);
 448             return new Expression(oldInstance, Collections.class, "unmodifiableMap", new Object[]{map});
 449         }
 450     }
 451 
 452     static final class UnmodifiableSortedMap_PersistenceDelegate extends java_util_Collections {
 453         protected Expression instantiate(Object oldInstance, Encoder out) {
 454             SortedMap map = new TreeMap((SortedMap) oldInstance);
 455             return new Expression(oldInstance, Collections.class, "unmodifiableSortedMap", new Object[]{map});
 456         }
 457     }
 458 
 459     static final class SynchronizedCollection_PersistenceDelegate extends java_util_Collections {
 460         protected Expression instantiate(Object oldInstance, Encoder out) {
 461             List list = new ArrayList((Collection) oldInstance);
 462             return new Expression(oldInstance, Collections.class, "synchronizedCollection", new Object[]{list});
 463         }
 464     }
 465 
 466     static final class SynchronizedList_PersistenceDelegate extends java_util_Collections {
 467         protected Expression instantiate(Object oldInstance, Encoder out) {
 468             List list = new LinkedList((Collection) oldInstance);
 469             return new Expression(oldInstance, Collections.class, "synchronizedList", new Object[]{list});
 470         }
 471     }
 472 
 473     static final class SynchronizedRandomAccessList_PersistenceDelegate extends java_util_Collections {
 474         protected Expression instantiate(Object oldInstance, Encoder out) {
 475             List list = new ArrayList((Collection) oldInstance);
 476             return new Expression(oldInstance, Collections.class, "synchronizedList", new Object[]{list});
 477         }
 478     }
 479 
 480     static final class SynchronizedSet_PersistenceDelegate extends java_util_Collections {
 481         protected Expression instantiate(Object oldInstance, Encoder out) {
 482             Set set = new HashSet((Set) oldInstance);
 483             return new Expression(oldInstance, Collections.class, "synchronizedSet", new Object[]{set});
 484         }
 485     }
 486 
 487     static final class SynchronizedSortedSet_PersistenceDelegate extends java_util_Collections {
 488         protected Expression instantiate(Object oldInstance, Encoder out) {
 489             SortedSet set = new TreeSet((SortedSet) oldInstance);
 490             return new Expression(oldInstance, Collections.class, "synchronizedSortedSet", new Object[]{set});
 491         }
 492     }
 493 
 494     static final class SynchronizedMap_PersistenceDelegate extends java_util_Collections {
 495         protected Expression instantiate(Object oldInstance, Encoder out) {
 496             Map map = new HashMap((Map) oldInstance);
 497             return new Expression(oldInstance, Collections.class, "synchronizedMap", new Object[]{map});
 498         }
 499     }
 500 
 501     static final class SynchronizedSortedMap_PersistenceDelegate extends java_util_Collections {
 502         protected Expression instantiate(Object oldInstance, Encoder out) {
 503             SortedMap map = new TreeMap((SortedMap) oldInstance);
 504             return new Expression(oldInstance, Collections.class, "synchronizedSortedMap", new Object[]{map});
 505         }
 506     }
 507 
 508     static final class CheckedCollection_PersistenceDelegate extends java_util_Collections {
 509         protected Expression instantiate(Object oldInstance, Encoder out) {
 510             Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
 511             List list = new ArrayList((Collection) oldInstance);
 512             return new Expression(oldInstance, Collections.class, "checkedCollection", new Object[]{list, type});
 513         }
 514     }
 515 
 516     static final class CheckedList_PersistenceDelegate extends java_util_Collections {
 517         protected Expression instantiate(Object oldInstance, Encoder out) {
 518             Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
 519             List list = new LinkedList((Collection) oldInstance);
 520             return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type});
 521         }
 522     }
 523 
 524     static final class CheckedRandomAccessList_PersistenceDelegate extends java_util_Collections {
 525         protected Expression instantiate(Object oldInstance, Encoder out) {
 526             Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
 527             List list = new ArrayList((Collection) oldInstance);
 528             return new Expression(oldInstance, Collections.class, "checkedList", new Object[]{list, type});
 529         }
 530     }
 531 
 532     static final class CheckedSet_PersistenceDelegate extends java_util_Collections {
 533         protected Expression instantiate(Object oldInstance, Encoder out) {
 534             Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
 535             Set set = new HashSet((Set) oldInstance);
 536             return new Expression(oldInstance, Collections.class, "checkedSet", new Object[]{set, type});
 537         }
 538     }
 539 
 540     static final class CheckedSortedSet_PersistenceDelegate extends java_util_Collections {
 541         protected Expression instantiate(Object oldInstance, Encoder out) {
 542             Object type = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedCollection.type");
 543             SortedSet set = new TreeSet((SortedSet) oldInstance);
 544             return new Expression(oldInstance, Collections.class, "checkedSortedSet", new Object[]{set, type});
 545         }
 546     }
 547 
 548     static final class CheckedMap_PersistenceDelegate extends java_util_Collections {
 549         protected Expression instantiate(Object oldInstance, Encoder out) {
 550             Object keyType   = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType");
 551             Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType");
 552             Map map = new HashMap((Map) oldInstance);
 553             return new Expression(oldInstance, Collections.class, "checkedMap", new Object[]{map, keyType, valueType});
 554         }
 555     }
 556 
 557     static final class CheckedSortedMap_PersistenceDelegate extends java_util_Collections {
 558         protected Expression instantiate(Object oldInstance, Encoder out) {
 559             Object keyType   = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.keyType");
 560             Object valueType = MetaData.getPrivateFieldValue(oldInstance, "java.util.Collections$CheckedMap.valueType");
 561             SortedMap map = new TreeMap((SortedMap) oldInstance);
 562             return new Expression(oldInstance, Collections.class, "checkedSortedMap", new Object[]{map, keyType, valueType});
 563         }
 564     }
 565 }
 566 
 567 /**
 568  * The persistence delegate for <CODE>java.util.EnumMap</CODE> classes.
 569  *
 570  * @author Sergey A. Malenkov
 571  */
 572 class java_util_EnumMap_PersistenceDelegate extends PersistenceDelegate {
 573     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 574         return super.mutatesTo(oldInstance, newInstance) && (getType(oldInstance) == getType(newInstance));
 575     }
 576 
 577     protected Expression instantiate(Object oldInstance, Encoder out) {
 578         return new Expression(oldInstance, EnumMap.class, "new", new Object[] {getType(oldInstance)});
 579     }
 580 
 581     private static Object getType(Object instance) {
 582         return MetaData.getPrivateFieldValue(instance, "java.util.EnumMap.keyType");
 583     }
 584 }
 585 
 586 /**
 587  * The persistence delegate for <CODE>java.util.EnumSet</CODE> classes.
 588  *
 589  * @author Sergey A. Malenkov
 590  */
 591 class java_util_EnumSet_PersistenceDelegate extends PersistenceDelegate {
 592     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 593         return super.mutatesTo(oldInstance, newInstance) && (getType(oldInstance) == getType(newInstance));
 594     }
 595 
 596     protected Expression instantiate(Object oldInstance, Encoder out) {
 597         return new Expression(oldInstance, EnumSet.class, "noneOf", new Object[] {getType(oldInstance)});
 598     }
 599 
 600     private static Object getType(Object instance) {
 601         return MetaData.getPrivateFieldValue(instance, "java.util.EnumSet.elementType");
 602     }
 603 }
 604 
 605 // Collection
 606 class java_util_Collection_PersistenceDelegate extends DefaultPersistenceDelegate {
 607     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 608         java.util.Collection oldO = (java.util.Collection)oldInstance;
 609         java.util.Collection newO = (java.util.Collection)newInstance;
 610 
 611         if (newO.size() != 0) {
 612             invokeStatement(oldInstance, "clear", new Object[]{}, out);
 613         }
 614         for (Iterator i = oldO.iterator(); i.hasNext();) {
 615             invokeStatement(oldInstance, "add", new Object[]{i.next()}, out);
 616         }
 617     }
 618 }
 619 
 620 // List
 621 class java_util_List_PersistenceDelegate extends DefaultPersistenceDelegate {
 622     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 623         java.util.List oldO = (java.util.List)oldInstance;
 624         java.util.List newO = (java.util.List)newInstance;
 625         int oldSize = oldO.size();
 626         int newSize = (newO == null) ? 0 : newO.size();
 627         if (oldSize < newSize) {
 628             invokeStatement(oldInstance, "clear", new Object[]{}, out);
 629             newSize = 0;
 630         }
 631         for (int i = 0; i < newSize; i++) {
 632             Object index = new Integer(i);
 633 
 634             Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index});
 635             Expression newGetExp = new Expression(newInstance, "get", new Object[]{index});
 636             try {
 637                 Object oldValue = oldGetExp.getValue();
 638                 Object newValue = newGetExp.getValue();
 639                 out.writeExpression(oldGetExp);
 640                 if (!Objects.equals(newValue, out.get(oldValue))) {
 641                     invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out);
 642                 }
 643             }
 644             catch (Exception e) {
 645                 out.getExceptionListener().exceptionThrown(e);
 646             }
 647         }
 648         for (int i = newSize; i < oldSize; i++) {
 649             invokeStatement(oldInstance, "add", new Object[]{oldO.get(i)}, out);
 650         }
 651     }
 652 }
 653 
 654 
 655 // Map
 656 class java_util_Map_PersistenceDelegate extends DefaultPersistenceDelegate {
 657     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 658         // System.out.println("Initializing: " + newInstance);
 659         java.util.Map oldMap = (java.util.Map)oldInstance;
 660         java.util.Map newMap = (java.util.Map)newInstance;
 661         // Remove the new elements.
 662         // Do this first otherwise we undo the adding work.
 663         if (newMap != null) {
 664             for (Object newKey : newMap.keySet().toArray()) {
 665                // PENDING: This "key" is not in the right environment.
 666                 if (!oldMap.containsKey(newKey)) {
 667                     invokeStatement(oldInstance, "remove", new Object[]{newKey}, out);
 668                 }
 669             }
 670         }
 671         // Add the new elements.
 672         for ( Object oldKey : oldMap.keySet() ) {
 673             Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{oldKey});
 674             // Pending: should use newKey.
 675             Expression newGetExp = new Expression(newInstance, "get", new Object[]{oldKey});
 676             try {
 677                 Object oldValue = oldGetExp.getValue();
 678                 Object newValue = newGetExp.getValue();
 679                 out.writeExpression(oldGetExp);
 680                 if (!Objects.equals(newValue, out.get(oldValue))) {
 681                     invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out);
 682                 } else if ((newValue == null) && !newMap.containsKey(oldKey)) {
 683                     // put oldValue(=null?) if oldKey is absent in newMap
 684                     invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out);
 685                 }
 686             }
 687             catch (Exception e) {
 688                 out.getExceptionListener().exceptionThrown(e);
 689             }
 690         }
 691     }
 692 }
 693 
 694 class java_util_AbstractCollection_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
 695 class java_util_AbstractList_PersistenceDelegate extends java_util_List_PersistenceDelegate {}
 696 class java_util_AbstractMap_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
 697 class java_util_Hashtable_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
 698 
 699 
 700 // Beans
 701 class java_beans_beancontext_BeanContextSupport_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
 702 
 703 // AWT
 704 
 705 /**
 706  * The persistence delegate for {@link Insets}.
 707  * It is impossible to use {@link DefaultPersistenceDelegate}
 708  * because this class does not have any properties.
 709  *
 710  * @author Sergey A. Malenkov
 711  */
 712 final class java_awt_Insets_PersistenceDelegate extends PersistenceDelegate {
 713     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 714         return oldInstance.equals(newInstance);
 715     }
 716 
 717     protected Expression instantiate(Object oldInstance, Encoder out) {
 718         Insets insets = (Insets) oldInstance;
 719         Object[] args = new Object[] {
 720                 insets.top,
 721                 insets.left,
 722                 insets.bottom,
 723                 insets.right,
 724         };
 725         return new Expression(insets, insets.getClass(), "new", args);
 726     }
 727 }
 728 
 729 /**
 730  * The persistence delegate for {@link Font}.
 731  * It is impossible to use {@link DefaultPersistenceDelegate}
 732  * because size of the font can be float value.
 733  *
 734  * @author Sergey A. Malenkov
 735  */
 736 final class java_awt_Font_PersistenceDelegate extends PersistenceDelegate {
 737     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 738         return oldInstance.equals(newInstance);
 739     }
 740 
 741     protected Expression instantiate(Object oldInstance, Encoder out) {
 742         Font font = (Font) oldInstance;
 743 
 744         int count = 0;
 745         String family = null;
 746         int style = Font.PLAIN;
 747         int size = 12;
 748 
 749         Map basic = font.getAttributes();
 750         Map clone = new HashMap(basic.size());
 751         for (Object key : basic.keySet()) {
 752             Object value = basic.get(key);
 753             if (value != null) {
 754                 clone.put(key, value);
 755             }
 756             if (key == TextAttribute.FAMILY) {
 757                 if (value instanceof String) {
 758                     count++;
 759                     family = (String) value;
 760                 }
 761             }
 762             else if (key == TextAttribute.WEIGHT) {
 763                 if (TextAttribute.WEIGHT_REGULAR.equals(value)) {
 764                     count++;
 765                 } else if (TextAttribute.WEIGHT_BOLD.equals(value)) {
 766                     count++;
 767                     style |= Font.BOLD;
 768                 }
 769             }
 770             else if (key == TextAttribute.POSTURE) {
 771                 if (TextAttribute.POSTURE_REGULAR.equals(value)) {
 772                     count++;
 773                 } else if (TextAttribute.POSTURE_OBLIQUE.equals(value)) {
 774                     count++;
 775                     style |= Font.ITALIC;
 776                 }
 777             } else if (key == TextAttribute.SIZE) {
 778                 if (value instanceof Number) {
 779                     Number number = (Number) value;
 780                     size = number.intValue();
 781                     if (size == number.floatValue()) {
 782                         count++;
 783                     }
 784                 }
 785             }
 786         }
 787         Class type = font.getClass();
 788         if (count == clone.size()) {
 789             return new Expression(font, type, "new", new Object[]{family, style, size});
 790         }
 791         if (type == Font.class) {
 792             return new Expression(font, type, "getFont", new Object[]{clone});
 793         }
 794         return new Expression(font, type, "new", new Object[]{Font.getFont(clone)});
 795     }
 796 }
 797 
 798 /**
 799  * The persistence delegate for {@link AWTKeyStroke}.
 800  * It is impossible to use {@link DefaultPersistenceDelegate}
 801  * because this class have no public constructor.
 802  *
 803  * @author Sergey A. Malenkov
 804  */
 805 final class java_awt_AWTKeyStroke_PersistenceDelegate extends PersistenceDelegate {
 806     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 807         return oldInstance.equals(newInstance);
 808     }
 809 
 810     protected Expression instantiate(Object oldInstance, Encoder out) {
 811         AWTKeyStroke key = (AWTKeyStroke) oldInstance;
 812 
 813         char ch = key.getKeyChar();
 814         int code = key.getKeyCode();
 815         int mask = key.getModifiers();
 816         boolean onKeyRelease = key.isOnKeyRelease();
 817 
 818         Object[] args = null;
 819         if (ch == KeyEvent.CHAR_UNDEFINED) {
 820             args = !onKeyRelease
 821                     ? new Object[]{code, mask}
 822                     : new Object[]{code, mask, onKeyRelease};
 823         } else if (code == KeyEvent.VK_UNDEFINED) {
 824             if (!onKeyRelease) {
 825                 args = (mask == 0)
 826                         ? new Object[]{ch}
 827                         : new Object[]{ch, mask};
 828             } else if (mask == 0) {
 829                 args = new Object[]{ch, onKeyRelease};
 830             }
 831         }
 832         if (args == null) {
 833             throw new IllegalStateException("Unsupported KeyStroke: " + key);
 834         }
 835         Class type = key.getClass();
 836         String name = type.getName();
 837         // get short name of the class
 838         int index = name.lastIndexOf('.') + 1;
 839         if (index > 0) {
 840             name = name.substring(index);
 841         }
 842         return new Expression( key, type, "get" + name, args );
 843     }
 844 }
 845 
 846 class StaticFieldsPersistenceDelegate extends PersistenceDelegate {
 847     protected void installFields(Encoder out, Class<?> cls) {
 848         Field fields[] = cls.getFields();
 849         for(int i = 0; i < fields.length; i++) {
 850             Field field = fields[i];
 851             // Don't install primitives, their identity will not be preserved
 852             // by wrapping.
 853             if (Object.class.isAssignableFrom(field.getType())) {
 854                 out.writeExpression(new Expression(field, "get", new Object[]{null}));
 855             }
 856         }
 857     }
 858 
 859     protected Expression instantiate(Object oldInstance, Encoder out) {
 860         throw new RuntimeException("Unrecognized instance: " + oldInstance);
 861     }
 862 
 863     public void writeObject(Object oldInstance, Encoder out) {
 864         if (out.getAttribute(this) == null) {
 865             out.setAttribute(this, Boolean.TRUE);
 866             installFields(out, oldInstance.getClass());
 867         }
 868         super.writeObject(oldInstance, out);
 869     }
 870 }
 871 
 872 // SystemColor
 873 class java_awt_SystemColor_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
 874 
 875 // TextAttribute
 876 class java_awt_font_TextAttribute_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
 877 
 878 // MenuShortcut
 879 class java_awt_MenuShortcut_PersistenceDelegate extends PersistenceDelegate {
 880     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 881         return oldInstance.equals(newInstance);
 882     }
 883 
 884     protected Expression instantiate(Object oldInstance, Encoder out) {
 885         java.awt.MenuShortcut m = (java.awt.MenuShortcut)oldInstance;
 886         return new Expression(oldInstance, m.getClass(), "new",
 887                    new Object[]{new Integer(m.getKey()), Boolean.valueOf(m.usesShiftModifier())});
 888     }
 889 }
 890 
 891 // Component
 892 class java_awt_Component_PersistenceDelegate extends DefaultPersistenceDelegate {
 893     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 894         super.initialize(type, oldInstance, newInstance, out);
 895         java.awt.Component c = (java.awt.Component)oldInstance;
 896         java.awt.Component c2 = (java.awt.Component)newInstance;
 897         // The "background", "foreground" and "font" properties.
 898         // The foreground and font properties of Windows change from
 899         // null to defined values after the Windows are made visible -
 900         // special case them for now.
 901         if (!(oldInstance instanceof java.awt.Window)) {
 902             Object oldBackground = c.isBackgroundSet() ? c.getBackground() : null;
 903             Object newBackground = c2.isBackgroundSet() ? c2.getBackground() : null;
 904             if (!Objects.equals(oldBackground, newBackground)) {
 905                 invokeStatement(oldInstance, "setBackground", new Object[] { oldBackground }, out);
 906             }
 907             Object oldForeground = c.isForegroundSet() ? c.getForeground() : null;
 908             Object newForeground = c2.isForegroundSet() ? c2.getForeground() : null;
 909             if (!Objects.equals(oldForeground, newForeground)) {
 910                 invokeStatement(oldInstance, "setForeground", new Object[] { oldForeground }, out);
 911             }
 912             Object oldFont = c.isFontSet() ? c.getFont() : null;
 913             Object newFont = c2.isFontSet() ? c2.getFont() : null;
 914             if (!Objects.equals(oldFont, newFont)) {
 915                 invokeStatement(oldInstance, "setFont", new Object[] { oldFont }, out);
 916             }
 917         }
 918 
 919         // Bounds
 920         java.awt.Container p = c.getParent();
 921         if (p == null || p.getLayout() == null) {
 922             // Use the most concise construct.
 923             boolean locationCorrect = c.getLocation().equals(c2.getLocation());
 924             boolean sizeCorrect = c.getSize().equals(c2.getSize());
 925             if (!locationCorrect && !sizeCorrect) {
 926                 invokeStatement(oldInstance, "setBounds", new Object[]{c.getBounds()}, out);
 927             }
 928             else if (!locationCorrect) {
 929                 invokeStatement(oldInstance, "setLocation", new Object[]{c.getLocation()}, out);
 930             }
 931             else if (!sizeCorrect) {
 932                 invokeStatement(oldInstance, "setSize", new Object[]{c.getSize()}, out);
 933             }
 934         }
 935     }
 936 }
 937 
 938 // Container
 939 class java_awt_Container_PersistenceDelegate extends DefaultPersistenceDelegate {
 940     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 941         super.initialize(type, oldInstance, newInstance, out);
 942         // Ignore the children of a JScrollPane.
 943         // Pending(milne) find a better way to do this.
 944         if (oldInstance instanceof javax.swing.JScrollPane) {
 945             return;
 946         }
 947         java.awt.Container oldC = (java.awt.Container)oldInstance;
 948         java.awt.Component[] oldChildren = oldC.getComponents();
 949         java.awt.Container newC = (java.awt.Container)newInstance;
 950         java.awt.Component[] newChildren = (newC == null) ? new java.awt.Component[0] : newC.getComponents();
 951 
 952         BorderLayout layout = ( oldC.getLayout() instanceof BorderLayout )
 953                 ? ( BorderLayout )oldC.getLayout()
 954                 : null;
 955 
 956         JLayeredPane oldLayeredPane = (oldInstance instanceof JLayeredPane)
 957                 ? (JLayeredPane) oldInstance
 958                 : null;
 959 
 960         // Pending. Assume all the new children are unaltered.
 961         for(int i = newChildren.length; i < oldChildren.length; i++) {
 962             Object[] args = ( layout != null )
 963                     ? new Object[] {oldChildren[i], layout.getConstraints( oldChildren[i] )}
 964                     : (oldLayeredPane != null)
 965                             ? new Object[] {oldChildren[i], oldLayeredPane.getLayer(oldChildren[i]), Integer.valueOf(-1)}
 966                             : new Object[] {oldChildren[i]};
 967 
 968             invokeStatement(oldInstance, "add", args, out);
 969         }
 970     }
 971 }
 972 
 973 // Choice
 974 class java_awt_Choice_PersistenceDelegate extends DefaultPersistenceDelegate {
 975     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 976         super.initialize(type, oldInstance, newInstance, out);
 977         java.awt.Choice m = (java.awt.Choice)oldInstance;
 978         java.awt.Choice n = (java.awt.Choice)newInstance;
 979         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
 980             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
 981         }
 982     }
 983 }
 984 
 985 // Menu
 986 class java_awt_Menu_PersistenceDelegate extends DefaultPersistenceDelegate {
 987     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 988         super.initialize(type, oldInstance, newInstance, out);
 989         java.awt.Menu m = (java.awt.Menu)oldInstance;
 990         java.awt.Menu n = (java.awt.Menu)newInstance;
 991         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
 992             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
 993         }
 994     }
 995 }
 996 
 997 // MenuBar
 998 class java_awt_MenuBar_PersistenceDelegate extends DefaultPersistenceDelegate {
 999     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1000         super.initialize(type, oldInstance, newInstance, out);
1001         java.awt.MenuBar m = (java.awt.MenuBar)oldInstance;
1002         java.awt.MenuBar n = (java.awt.MenuBar)newInstance;
1003         for (int i = n.getMenuCount(); i < m.getMenuCount(); i++) {
1004             invokeStatement(oldInstance, "add", new Object[]{m.getMenu(i)}, out);
1005         }
1006     }
1007 }
1008 
1009 // List
1010 class java_awt_List_PersistenceDelegate extends DefaultPersistenceDelegate {
1011     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1012         super.initialize(type, oldInstance, newInstance, out);
1013         java.awt.List m = (java.awt.List)oldInstance;
1014         java.awt.List n = (java.awt.List)newInstance;
1015         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
1016             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
1017         }
1018     }
1019 }
1020 
1021 
1022 // LayoutManagers
1023 
1024 // BorderLayout
1025 class java_awt_BorderLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
1026     private static final String[] CONSTRAINTS = {
1027             BorderLayout.NORTH,
1028             BorderLayout.SOUTH,
1029             BorderLayout.EAST,
1030             BorderLayout.WEST,
1031             BorderLayout.CENTER,
1032             BorderLayout.PAGE_START,
1033             BorderLayout.PAGE_END,
1034             BorderLayout.LINE_START,
1035             BorderLayout.LINE_END,
1036     };
1037     @Override
1038     protected void initialize(Class<?> type, Object oldInstance,
1039                               Object newInstance, Encoder out) {
1040         super.initialize(type, oldInstance, newInstance, out);
1041         BorderLayout oldLayout = (BorderLayout) oldInstance;
1042         BorderLayout newLayout = (BorderLayout) newInstance;
1043         for (String constraints : CONSTRAINTS) {
1044             Object oldC = oldLayout.getLayoutComponent(constraints);
1045             Object newC = newLayout.getLayoutComponent(constraints);
1046             // Pending, assume any existing elements are OK.
1047             if (oldC != null && newC == null) {
1048                 invokeStatement(oldInstance, "addLayoutComponent",
1049                                 new Object[] { oldC, constraints }, out);
1050             }
1051         }
1052     }
1053 }
1054 
1055 // CardLayout
1056 class java_awt_CardLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
1057     protected void initialize(Class<?> type, Object oldInstance,
1058                               Object newInstance, Encoder out) {
1059         super.initialize(type, oldInstance, newInstance, out);
1060         Hashtable tab = (Hashtable)ReflectionUtils.getPrivateField(oldInstance,
1061                                                                    java.awt.CardLayout.class,
1062                                                                    "tab",
1063                                                                    out.getExceptionListener());
1064         if (tab != null) {
1065             for(Enumeration e = tab.keys(); e.hasMoreElements();) {
1066                 Object child = e.nextElement();
1067                 invokeStatement(oldInstance, "addLayoutComponent",
1068                                 new Object[]{child, (String)tab.get(child)}, out);
1069             }
1070         }
1071     }
1072 }
1073 
1074 // GridBagLayout
1075 class java_awt_GridBagLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
1076     protected void initialize(Class<?> type, Object oldInstance,
1077                               Object newInstance, Encoder out) {
1078         super.initialize(type, oldInstance, newInstance, out);
1079         Hashtable comptable = (Hashtable)ReflectionUtils.getPrivateField(oldInstance,
1080                                                  java.awt.GridBagLayout.class,
1081                                                  "comptable",
1082                                                  out.getExceptionListener());
1083         if (comptable != null) {
1084             for(Enumeration e = comptable.keys(); e.hasMoreElements();) {
1085                 Object child = e.nextElement();
1086                 invokeStatement(oldInstance, "addLayoutComponent",
1087                                 new Object[]{child, comptable.get(child)}, out);
1088             }
1089         }
1090     }
1091 }
1092 
1093 // Swing
1094 
1095 // JFrame (If we do this for Window instead of JFrame, the setVisible call
1096 // will be issued before we have added all the children to the JFrame and
1097 // will appear blank).
1098 class javax_swing_JFrame_PersistenceDelegate extends DefaultPersistenceDelegate {
1099     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1100         super.initialize(type, oldInstance, newInstance, out);
1101         java.awt.Window oldC = (java.awt.Window)oldInstance;
1102         java.awt.Window newC = (java.awt.Window)newInstance;
1103         boolean oldV = oldC.isVisible();
1104         boolean newV = newC.isVisible();
1105         if (newV != oldV) {
1106             // false means: don't execute this statement at write time.
1107             boolean executeStatements = out.executeStatements;
1108             out.executeStatements = false;
1109             invokeStatement(oldInstance, "setVisible", new Object[]{Boolean.valueOf(oldV)}, out);
1110             out.executeStatements = executeStatements;
1111         }
1112     }
1113 }
1114 
1115 // Models
1116 
1117 // DefaultListModel
1118 class javax_swing_DefaultListModel_PersistenceDelegate extends DefaultPersistenceDelegate {
1119     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1120         // Note, the "size" property will be set here.
1121         super.initialize(type, oldInstance, newInstance, out);
1122         javax.swing.DefaultListModel m = (javax.swing.DefaultListModel)oldInstance;
1123         javax.swing.DefaultListModel n = (javax.swing.DefaultListModel)newInstance;
1124         for (int i = n.getSize(); i < m.getSize(); i++) {
1125             invokeStatement(oldInstance, "add", // Can also use "addElement".
1126                     new Object[]{m.getElementAt(i)}, out);
1127         }
1128     }
1129 }
1130 
1131 // DefaultComboBoxModel
1132 class javax_swing_DefaultComboBoxModel_PersistenceDelegate extends DefaultPersistenceDelegate {
1133     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1134         super.initialize(type, oldInstance, newInstance, out);
1135         javax.swing.DefaultComboBoxModel m = (javax.swing.DefaultComboBoxModel)oldInstance;
1136         for (int i = 0; i < m.getSize(); i++) {
1137             invokeStatement(oldInstance, "addElement", new Object[]{m.getElementAt(i)}, out);
1138         }
1139     }
1140 }
1141 
1142 
1143 // DefaultMutableTreeNode
1144 class javax_swing_tree_DefaultMutableTreeNode_PersistenceDelegate extends DefaultPersistenceDelegate {
1145     protected void initialize(Class<?> type, Object oldInstance, Object
1146                               newInstance, Encoder out) {
1147         super.initialize(type, oldInstance, newInstance, out);
1148         javax.swing.tree.DefaultMutableTreeNode m =
1149             (javax.swing.tree.DefaultMutableTreeNode)oldInstance;
1150         javax.swing.tree.DefaultMutableTreeNode n =
1151             (javax.swing.tree.DefaultMutableTreeNode)newInstance;
1152         for (int i = n.getChildCount(); i < m.getChildCount(); i++) {
1153             invokeStatement(oldInstance, "add", new
1154                 Object[]{m.getChildAt(i)}, out);
1155         }
1156     }
1157 }
1158 
1159 // ToolTipManager
1160 class javax_swing_ToolTipManager_PersistenceDelegate extends PersistenceDelegate {
1161     protected Expression instantiate(Object oldInstance, Encoder out) {
1162         return new Expression(oldInstance, javax.swing.ToolTipManager.class,
1163                               "sharedInstance", new Object[]{});
1164     }
1165 }
1166 
1167 // JTabbedPane
1168 class javax_swing_JTabbedPane_PersistenceDelegate extends DefaultPersistenceDelegate {
1169     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1170         super.initialize(type, oldInstance, newInstance, out);
1171         javax.swing.JTabbedPane p = (javax.swing.JTabbedPane)oldInstance;
1172         for (int i = 0; i < p.getTabCount(); i++) {
1173             invokeStatement(oldInstance, "addTab",
1174                                           new Object[]{
1175                                               p.getTitleAt(i),
1176                                               p.getIconAt(i),
1177                                               p.getComponentAt(i)}, out);
1178         }
1179     }
1180 }
1181 
1182 // Box
1183 class javax_swing_Box_PersistenceDelegate extends DefaultPersistenceDelegate {
1184     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
1185         return super.mutatesTo(oldInstance, newInstance) && getAxis(oldInstance).equals(getAxis(newInstance));
1186     }
1187 
1188     protected Expression instantiate(Object oldInstance, Encoder out) {
1189         return new Expression(oldInstance, oldInstance.getClass(), "new", new Object[] {getAxis(oldInstance)});
1190     }
1191 
1192     private Integer getAxis(Object object) {
1193         Box box = (Box) object;
1194         return (Integer) MetaData.getPrivateFieldValue(box.getLayout(), "javax.swing.BoxLayout.axis");
1195     }
1196 }
1197 
1198 // JMenu
1199 // Note that we do not need to state the initialiser for
1200 // JMenuItems since the getComponents() method defined in
1201 // Container will return all of the sub menu items that
1202 // need to be added to the menu item.
1203 // Not so for JMenu apparently.
1204 class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate {
1205     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1206         super.initialize(type, oldInstance, newInstance, out);
1207         javax.swing.JMenu m = (javax.swing.JMenu)oldInstance;
1208         java.awt.Component[] c = m.getMenuComponents();
1209         for (int i = 0; i < c.length; i++) {
1210             invokeStatement(oldInstance, "add", new Object[]{c[i]}, out);
1211         }
1212     }
1213 }
1214 
1215 /**
1216  * The persistence delegate for {@link MatteBorder}.
1217  * It is impossible to use {@link DefaultPersistenceDelegate}
1218  * because this class does not have writable properties.
1219  *
1220  * @author Sergey A. Malenkov
1221  */
1222 final class javax_swing_border_MatteBorder_PersistenceDelegate extends PersistenceDelegate {
1223     protected Expression instantiate(Object oldInstance, Encoder out) {
1224         MatteBorder border = (MatteBorder) oldInstance;
1225         Insets insets = border.getBorderInsets();
1226         Object object = border.getTileIcon();
1227         if (object == null) {
1228             object = border.getMatteColor();
1229         }
1230         Object[] args = new Object[] {
1231                 insets.top,
1232                 insets.left,
1233                 insets.bottom,
1234                 insets.right,
1235                 object,
1236         };
1237         return new Expression(border, border.getClass(), "new", args);
1238     }
1239 }
1240 
1241 /* XXX - doens't seem to work. Debug later.
1242 class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate {
1243     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1244         super.initialize(type, oldInstance, newInstance, out);
1245         javax.swing.JMenu m = (javax.swing.JMenu)oldInstance;
1246         javax.swing.JMenu n = (javax.swing.JMenu)newInstance;
1247         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
1248             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
1249         }
1250     }
1251 }
1252 */
1253 
1254 /**
1255  * The persistence delegate for {@link PrintColorUIResource}.
1256  * It is impossible to use {@link DefaultPersistenceDelegate}
1257  * because this class has special rule for serialization:
1258  * it should be converted to {@link ColorUIResource}.
1259  *
1260  * @see PrintColorUIResource#writeReplace
1261  *
1262  * @author Sergey A. Malenkov
1263  */
1264 final class sun_swing_PrintColorUIResource_PersistenceDelegate extends PersistenceDelegate {
1265     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
1266         return oldInstance.equals(newInstance);
1267     }
1268 
1269     protected Expression instantiate(Object oldInstance, Encoder out) {
1270         Color color = (Color) oldInstance;
1271         Object[] args = new Object[] {color.getRGB()};
1272         return new Expression(color, ColorUIResource.class, "new", args);
1273     }
1274 }
1275 
1276 class MetaData {
1277     private static final Map<String,Field> fields = Collections.synchronizedMap(new WeakHashMap<String, Field>());
1278     private static Hashtable internalPersistenceDelegates = new Hashtable();
1279 
1280     private static PersistenceDelegate nullPersistenceDelegate = new NullPersistenceDelegate();
1281     private static PersistenceDelegate enumPersistenceDelegate = new EnumPersistenceDelegate();
1282     private static PersistenceDelegate primitivePersistenceDelegate = new PrimitivePersistenceDelegate();
1283     private static PersistenceDelegate defaultPersistenceDelegate = new DefaultPersistenceDelegate();
1284     private static PersistenceDelegate arrayPersistenceDelegate;
1285     private static PersistenceDelegate proxyPersistenceDelegate;
1286 
1287     static {
1288 
1289         internalPersistenceDelegates.put("java.net.URI",
1290                                          new PrimitivePersistenceDelegate());
1291 
1292         // it is possible because MatteBorder is assignable from MatteBorderUIResource
1293         internalPersistenceDelegates.put("javax.swing.plaf.BorderUIResource$MatteBorderUIResource",
1294                                          new javax_swing_border_MatteBorder_PersistenceDelegate());
1295 
1296         // it is possible because FontUIResource is supported by java_awt_Font_PersistenceDelegate
1297         internalPersistenceDelegates.put("javax.swing.plaf.FontUIResource",
1298                                          new java_awt_Font_PersistenceDelegate());
1299 
1300         // it is possible because KeyStroke is supported by java_awt_AWTKeyStroke_PersistenceDelegate
1301         internalPersistenceDelegates.put("javax.swing.KeyStroke",
1302                                          new java_awt_AWTKeyStroke_PersistenceDelegate());
1303 
1304         internalPersistenceDelegates.put("java.sql.Date", new java_util_Date_PersistenceDelegate());
1305         internalPersistenceDelegates.put("java.sql.Time", new java_util_Date_PersistenceDelegate());
1306 
1307         internalPersistenceDelegates.put("java.util.JumboEnumSet", new java_util_EnumSet_PersistenceDelegate());
1308         internalPersistenceDelegates.put("java.util.RegularEnumSet", new java_util_EnumSet_PersistenceDelegate());
1309     }
1310 
1311     public synchronized static PersistenceDelegate getPersistenceDelegate(Class type) {
1312         if (type == null) {
1313             return nullPersistenceDelegate;
1314         }
1315         if (Enum.class.isAssignableFrom(type)) {
1316             return enumPersistenceDelegate;
1317         }
1318         if (ReflectionUtils.isPrimitive(type)) {
1319             return primitivePersistenceDelegate;
1320         }
1321         // The persistence delegate for arrays is non-trivial; instantiate it lazily.
1322         if (type.isArray()) {
1323             if (arrayPersistenceDelegate == null) {
1324                 arrayPersistenceDelegate = new ArrayPersistenceDelegate();
1325             }
1326             return arrayPersistenceDelegate;
1327         }
1328         // Handle proxies lazily for backward compatibility with 1.2.
1329         try {
1330             if (java.lang.reflect.Proxy.isProxyClass(type)) {
1331                 if (proxyPersistenceDelegate == null) {
1332                     proxyPersistenceDelegate = new ProxyPersistenceDelegate();
1333                 }
1334                 return proxyPersistenceDelegate;
1335             }
1336         }
1337         catch(Exception e) {}
1338         // else if (type.getDeclaringClass() != null) {
1339         //     return new DefaultPersistenceDelegate(new String[]{"this$0"});
1340         // }
1341 
1342         String typeName = type.getName();
1343         PersistenceDelegate pd = (PersistenceDelegate)getBeanAttribute(type, "persistenceDelegate");
1344         if (pd == null) {
1345             pd = (PersistenceDelegate)internalPersistenceDelegates.get(typeName);
1346             if (pd != null) {
1347                 return pd;
1348             }
1349             internalPersistenceDelegates.put(typeName, defaultPersistenceDelegate);
1350             try {
1351                 String name =  type.getName();
1352                 Class c = Class.forName("java.beans." + name.replace('.', '_')
1353                                         + "_PersistenceDelegate");
1354                 pd = (PersistenceDelegate)c.newInstance();
1355                 internalPersistenceDelegates.put(typeName, pd);
1356             }
1357             catch (ClassNotFoundException e) {
1358                 String[] properties = getConstructorProperties(type);
1359                 if (properties != null) {
1360                     pd = new DefaultPersistenceDelegate(properties);
1361                     internalPersistenceDelegates.put(typeName, pd);
1362                 }
1363             }
1364             catch (Exception e) {
1365                 System.err.println("Internal error: " + e);
1366             }
1367         }
1368 
1369         return (pd != null) ? pd : defaultPersistenceDelegate;
1370     }
1371 
1372     private static String[] getConstructorProperties(Class type) {
1373         String[] names = null;
1374         int length = 0;
1375         for (Constructor<?> constructor : type.getConstructors()) {
1376             String[] value = getAnnotationValue(constructor);
1377             if ((value != null) && (length < value.length) && isValid(constructor, value)) {
1378                 names = value;
1379                 length = value.length;
1380             }
1381         }
1382         return names;
1383     }
1384 
1385     private static String[] getAnnotationValue(Constructor<?> constructor) {
1386         ConstructorProperties annotation = constructor.getAnnotation(ConstructorProperties.class);
1387         return (annotation != null)
1388                 ? annotation.value()
1389                 : null;
1390     }
1391 
1392     private static boolean isValid(Constructor<?> constructor, String[] names) {
1393         Class[] parameters = constructor.getParameterTypes();
1394         if (names.length != parameters.length) {
1395             return false;
1396         }
1397         for (String name : names) {
1398             if (name == null) {
1399                 return false;
1400             }
1401         }
1402         return true;
1403     }
1404 
1405     private static Object getBeanAttribute(Class type, String attribute) {
1406         try {
1407             return Introspector.getBeanInfo(type).getBeanDescriptor().getValue(attribute);
1408         } catch (IntrospectionException exception) {
1409             return null;
1410         }
1411     }
1412 
1413     static Object getPrivateFieldValue(Object instance, String name) {
1414         Field field = fields.get(name);
1415         if (field == null) {
1416             int index = name.lastIndexOf('.');
1417             final String className = name.substring(0, index);
1418             final String fieldName = name.substring(1 + index);
1419             field = AccessController.doPrivileged(new PrivilegedAction<Field>() {
1420                 public Field run() {
1421                     try {
1422                         Field field = Class.forName(className).getDeclaredField(fieldName);
1423                         field.setAccessible(true);
1424                         return field;
1425                     }
1426                     catch (ClassNotFoundException exception) {
1427                         throw new IllegalStateException("Could not find class", exception);
1428                     }
1429                     catch (NoSuchFieldException exception) {
1430                         throw new IllegalStateException("Could not find field", exception);
1431                     }
1432                 }
1433             });
1434             fields.put(name, field);
1435         }
1436         try {
1437             return field.get(instance);
1438         }
1439         catch (IllegalAccessException exception) {
1440             throw new IllegalStateException("Could not get value of the field", exception);
1441         }
1442     }
1443 }