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