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