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, ClassLoader.getPlatformClassLoader());
 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 
 515 // Collection
 516 static class java_util_Collection_PersistenceDelegate extends DefaultPersistenceDelegate {
 517     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 518         java.util.Collection<?> oldO = (java.util.Collection)oldInstance;
 519         java.util.Collection<?> newO = (java.util.Collection)newInstance;
 520 
 521         if (newO.size() != 0) {
 522             invokeStatement(oldInstance, "clear", new Object[]{}, out);
 523         }
 524         for (Iterator<?> i = oldO.iterator(); i.hasNext();) {
 525             invokeStatement(oldInstance, "add", new Object[]{i.next()}, out);
 526         }
 527     }
 528 }
 529 
 530 // List
 531 static class java_util_List_PersistenceDelegate extends DefaultPersistenceDelegate {
 532     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 533         java.util.List<?> oldO = (java.util.List<?>)oldInstance;
 534         java.util.List<?> newO = (java.util.List<?>)newInstance;
 535         int oldSize = oldO.size();
 536         int newSize = (newO == null) ? 0 : newO.size();
 537         if (oldSize < newSize) {
 538             invokeStatement(oldInstance, "clear", new Object[]{}, out);
 539             newSize = 0;
 540         }
 541         for (int i = 0; i < newSize; i++) {
 542             Object index = i;
 543 
 544             Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{index});
 545             Expression newGetExp = new Expression(newInstance, "get", new Object[]{index});
 546             try {
 547                 Object oldValue = oldGetExp.getValue();
 548                 Object newValue = newGetExp.getValue();
 549                 out.writeExpression(oldGetExp);
 550                 if (!Objects.equals(newValue, out.get(oldValue))) {
 551                     invokeStatement(oldInstance, "set", new Object[]{index, oldValue}, out);
 552                 }
 553             }
 554             catch (Exception e) {
 555                 out.getExceptionListener().exceptionThrown(e);
 556             }
 557         }
 558         for (int i = newSize; i < oldSize; i++) {
 559             invokeStatement(oldInstance, "add", new Object[]{oldO.get(i)}, out);
 560         }
 561     }
 562 }
 563 
 564 
 565 // Map
 566 static class java_util_Map_PersistenceDelegate extends DefaultPersistenceDelegate {
 567     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 568         // System.out.println("Initializing: " + newInstance);
 569         java.util.Map<?,?> oldMap = (java.util.Map)oldInstance;
 570         java.util.Map<?,?> newMap = (java.util.Map)newInstance;
 571         // Remove the new elements.
 572         // Do this first otherwise we undo the adding work.
 573         if (newMap != null) {
 574             for (Object newKey : newMap.keySet().toArray()) {
 575                // PENDING: This "key" is not in the right environment.
 576                 if (!oldMap.containsKey(newKey)) {
 577                     invokeStatement(oldInstance, "remove", new Object[]{newKey}, out);
 578                 }
 579             }
 580         }
 581         // Add the new elements.
 582         for ( Object oldKey : oldMap.keySet() ) {
 583             Expression oldGetExp = new Expression(oldInstance, "get", new Object[]{oldKey});
 584             // Pending: should use newKey.
 585             Expression newGetExp = new Expression(newInstance, "get", new Object[]{oldKey});
 586             try {
 587                 Object oldValue = oldGetExp.getValue();
 588                 Object newValue = newGetExp.getValue();
 589                 out.writeExpression(oldGetExp);
 590                 if (!Objects.equals(newValue, out.get(oldValue))) {
 591                     invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out);
 592                 } else if ((newValue == null) && !newMap.containsKey(oldKey)) {
 593                     // put oldValue(=null?) if oldKey is absent in newMap
 594                     invokeStatement(oldInstance, "put", new Object[]{oldKey, oldValue}, out);
 595                 }
 596             }
 597             catch (Exception e) {
 598                 out.getExceptionListener().exceptionThrown(e);
 599             }
 600         }
 601     }
 602 }
 603 
 604 static final class java_util_AbstractCollection_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
 605 static final class java_util_AbstractList_PersistenceDelegate extends java_util_List_PersistenceDelegate {}
 606 static final class java_util_AbstractMap_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
 607 static final class java_util_Hashtable_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
 608 
 609 
 610 // Beans
 611 static final class java_beans_beancontext_BeanContextSupport_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
 612 
 613 // AWT
 614 
 615 /**
 616  * The persistence delegate for {@link Insets}.
 617  * It is impossible to use {@link DefaultPersistenceDelegate}
 618  * because this class does not have any properties.
 619  *
 620  * @author Sergey A. Malenkov
 621  */
 622 static final class java_awt_Insets_PersistenceDelegate extends PersistenceDelegate {
 623     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 624         return oldInstance.equals(newInstance);
 625     }
 626 
 627     protected Expression instantiate(Object oldInstance, Encoder out) {
 628         Insets insets = (Insets) oldInstance;
 629         Object[] args = new Object[] {
 630                 insets.top,
 631                 insets.left,
 632                 insets.bottom,
 633                 insets.right,
 634         };
 635         return new Expression(insets, insets.getClass(), "new", args);
 636     }
 637 }
 638 
 639 /**
 640  * The persistence delegate for {@link Font}.
 641  * It is impossible to use {@link DefaultPersistenceDelegate}
 642  * because size of the font can be float value.
 643  *
 644  * @author Sergey A. Malenkov
 645  */
 646 static final class java_awt_Font_PersistenceDelegate extends PersistenceDelegate {
 647     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 648         return oldInstance.equals(newInstance);
 649     }
 650 
 651     protected Expression instantiate(Object oldInstance, Encoder out) {
 652         Font font = (Font) oldInstance;
 653 
 654         int count = 0;
 655         String family = null;
 656         int style = Font.PLAIN;
 657         int size = 12;
 658 
 659         Map<TextAttribute, ?> basic = font.getAttributes();
 660         Map<TextAttribute, Object> clone = new HashMap<>(basic.size());
 661         for (TextAttribute key : basic.keySet()) {
 662             Object value = basic.get(key);
 663             if (value != null) {
 664                 clone.put(key, value);
 665             }
 666             if (key == TextAttribute.FAMILY) {
 667                 if (value instanceof String) {
 668                     count++;
 669                     family = (String) value;
 670                 }
 671             }
 672             else if (key == TextAttribute.WEIGHT) {
 673                 if (TextAttribute.WEIGHT_REGULAR.equals(value)) {
 674                     count++;
 675                 } else if (TextAttribute.WEIGHT_BOLD.equals(value)) {
 676                     count++;
 677                     style |= Font.BOLD;
 678                 }
 679             }
 680             else if (key == TextAttribute.POSTURE) {
 681                 if (TextAttribute.POSTURE_REGULAR.equals(value)) {
 682                     count++;
 683                 } else if (TextAttribute.POSTURE_OBLIQUE.equals(value)) {
 684                     count++;
 685                     style |= Font.ITALIC;
 686                 }
 687             } else if (key == TextAttribute.SIZE) {
 688                 if (value instanceof Number) {
 689                     Number number = (Number) value;
 690                     size = number.intValue();
 691                     if (size == number.floatValue()) {
 692                         count++;
 693                     }
 694                 }
 695             }
 696         }
 697         Class<?> type = font.getClass();
 698         if (count == clone.size()) {
 699             return new Expression(font, type, "new", new Object[]{family, style, size});
 700         }
 701         if (type == Font.class) {
 702             return new Expression(font, type, "getFont", new Object[]{clone});
 703         }
 704         return new Expression(font, type, "new", new Object[]{Font.getFont(clone)});
 705     }
 706 }
 707 
 708 /**
 709  * The persistence delegate for {@link AWTKeyStroke}.
 710  * It is impossible to use {@link DefaultPersistenceDelegate}
 711  * because this class have no public constructor.
 712  *
 713  * @author Sergey A. Malenkov
 714  */
 715 static final class java_awt_AWTKeyStroke_PersistenceDelegate extends PersistenceDelegate {
 716     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 717         return oldInstance.equals(newInstance);
 718     }
 719 
 720     protected Expression instantiate(Object oldInstance, Encoder out) {
 721         AWTKeyStroke key = (AWTKeyStroke) oldInstance;
 722 
 723         char ch = key.getKeyChar();
 724         int code = key.getKeyCode();
 725         int mask = key.getModifiers();
 726         boolean onKeyRelease = key.isOnKeyRelease();
 727 
 728         Object[] args = null;
 729         if (ch == KeyEvent.CHAR_UNDEFINED) {
 730             args = !onKeyRelease
 731                     ? new Object[]{code, mask}
 732                     : new Object[]{code, mask, onKeyRelease};
 733         } else if (code == KeyEvent.VK_UNDEFINED) {
 734             if (!onKeyRelease) {
 735                 args = (mask == 0)
 736                         ? new Object[]{ch}
 737                         : new Object[]{ch, mask};
 738             } else if (mask == 0) {
 739                 args = new Object[]{ch, onKeyRelease};
 740             }
 741         }
 742         if (args == null) {
 743             throw new IllegalStateException("Unsupported KeyStroke: " + key);
 744         }
 745         Class<?> type = key.getClass();
 746         String name = type.getName();
 747         // get short name of the class
 748         int index = name.lastIndexOf('.') + 1;
 749         if (index > 0) {
 750             name = name.substring(index);
 751         }
 752         return new Expression( key, type, "get" + name, args );
 753     }
 754 }
 755 
 756 static class StaticFieldsPersistenceDelegate extends PersistenceDelegate {
 757     protected void installFields(Encoder out, Class<?> cls) {
 758         if (Modifier.isPublic(cls.getModifiers()) && isPackageAccessible(cls)) {
 759             Field fields[] = cls.getFields();
 760             for(int i = 0; i < fields.length; i++) {
 761                 Field field = fields[i];
 762                 // Don't install primitives, their identity will not be preserved
 763                 // by wrapping.
 764                 if (Object.class.isAssignableFrom(field.getType())) {
 765                     out.writeExpression(new Expression(field, "get", new Object[]{null}));
 766                 }
 767             }
 768         }
 769     }
 770 
 771     protected Expression instantiate(Object oldInstance, Encoder out) {
 772         throw new RuntimeException("Unrecognized instance: " + oldInstance);
 773     }
 774 
 775     public void writeObject(Object oldInstance, Encoder out) {
 776         if (out.getAttribute(this) == null) {
 777             out.setAttribute(this, Boolean.TRUE);
 778             installFields(out, oldInstance.getClass());
 779         }
 780         super.writeObject(oldInstance, out);
 781     }
 782 }
 783 
 784 // SystemColor
 785 static final class java_awt_SystemColor_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
 786 
 787 // TextAttribute
 788 static final class java_awt_font_TextAttribute_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
 789 
 790 // MenuShortcut
 791 static final class java_awt_MenuShortcut_PersistenceDelegate extends PersistenceDelegate {
 792     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 793         return oldInstance.equals(newInstance);
 794     }
 795 
 796     protected Expression instantiate(Object oldInstance, Encoder out) {
 797         java.awt.MenuShortcut m = (java.awt.MenuShortcut)oldInstance;
 798         return new Expression(oldInstance, m.getClass(), "new",
 799                    new Object[]{m.getKey(), Boolean.valueOf(m.usesShiftModifier())});
 800     }
 801 }
 802 
 803 // Component
 804 static final class java_awt_Component_PersistenceDelegate extends DefaultPersistenceDelegate {
 805     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 806         super.initialize(type, oldInstance, newInstance, out);
 807         java.awt.Component c = (java.awt.Component)oldInstance;
 808         java.awt.Component c2 = (java.awt.Component)newInstance;
 809         // The "background", "foreground" and "font" properties.
 810         // The foreground and font properties of Windows change from
 811         // null to defined values after the Windows are made visible -
 812         // special case them for now.
 813         if (!(oldInstance instanceof java.awt.Window)) {
 814             Object oldBackground = c.isBackgroundSet() ? c.getBackground() : null;
 815             Object newBackground = c2.isBackgroundSet() ? c2.getBackground() : null;
 816             if (!Objects.equals(oldBackground, newBackground)) {
 817                 invokeStatement(oldInstance, "setBackground", new Object[] { oldBackground }, out);
 818             }
 819             Object oldForeground = c.isForegroundSet() ? c.getForeground() : null;
 820             Object newForeground = c2.isForegroundSet() ? c2.getForeground() : null;
 821             if (!Objects.equals(oldForeground, newForeground)) {
 822                 invokeStatement(oldInstance, "setForeground", new Object[] { oldForeground }, out);
 823             }
 824             Object oldFont = c.isFontSet() ? c.getFont() : null;
 825             Object newFont = c2.isFontSet() ? c2.getFont() : null;
 826             if (!Objects.equals(oldFont, newFont)) {
 827                 invokeStatement(oldInstance, "setFont", new Object[] { oldFont }, out);
 828             }
 829         }
 830 
 831         // Bounds
 832         java.awt.Container p = c.getParent();
 833         if (p == null || p.getLayout() == null) {
 834             // Use the most concise construct.
 835             boolean locationCorrect = c.getLocation().equals(c2.getLocation());
 836             boolean sizeCorrect = c.getSize().equals(c2.getSize());
 837             if (!locationCorrect && !sizeCorrect) {
 838                 invokeStatement(oldInstance, "setBounds", new Object[]{c.getBounds()}, out);
 839             }
 840             else if (!locationCorrect) {
 841                 invokeStatement(oldInstance, "setLocation", new Object[]{c.getLocation()}, out);
 842             }
 843             else if (!sizeCorrect) {
 844                 invokeStatement(oldInstance, "setSize", new Object[]{c.getSize()}, out);
 845             }
 846         }
 847     }
 848 }
 849 
 850 // Container
 851 static final class java_awt_Container_PersistenceDelegate extends DefaultPersistenceDelegate {
 852     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 853         super.initialize(type, oldInstance, newInstance, out);
 854         // Ignore the children of a JScrollPane.
 855         // Pending(milne) find a better way to do this.
 856         if (oldInstance instanceof javax.swing.JScrollPane) {
 857             return;
 858         }
 859         java.awt.Container oldC = (java.awt.Container)oldInstance;
 860         java.awt.Component[] oldChildren = oldC.getComponents();
 861         java.awt.Container newC = (java.awt.Container)newInstance;
 862         java.awt.Component[] newChildren = (newC == null) ? new java.awt.Component[0] : newC.getComponents();
 863 
 864         BorderLayout layout = ( oldC.getLayout() instanceof BorderLayout )
 865                 ? ( BorderLayout )oldC.getLayout()
 866                 : null;
 867 
 868         JLayeredPane oldLayeredPane = (oldInstance instanceof JLayeredPane)
 869                 ? (JLayeredPane) oldInstance
 870                 : null;
 871 
 872         // Pending. Assume all the new children are unaltered.
 873         for(int i = newChildren.length; i < oldChildren.length; i++) {
 874             Object[] args = ( layout != null )
 875                     ? new Object[] {oldChildren[i], layout.getConstraints( oldChildren[i] )}
 876                     : (oldLayeredPane != null)
 877                             ? new Object[] {oldChildren[i], oldLayeredPane.getLayer(oldChildren[i]), Integer.valueOf(-1)}
 878                             : new Object[] {oldChildren[i]};
 879 
 880             invokeStatement(oldInstance, "add", args, out);
 881         }
 882     }
 883 }
 884 
 885 // Choice
 886 static final class java_awt_Choice_PersistenceDelegate extends DefaultPersistenceDelegate {
 887     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 888         super.initialize(type, oldInstance, newInstance, out);
 889         java.awt.Choice m = (java.awt.Choice)oldInstance;
 890         java.awt.Choice n = (java.awt.Choice)newInstance;
 891         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
 892             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
 893         }
 894     }
 895 }
 896 
 897 // Menu
 898 static final class java_awt_Menu_PersistenceDelegate extends DefaultPersistenceDelegate {
 899     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 900         super.initialize(type, oldInstance, newInstance, out);
 901         java.awt.Menu m = (java.awt.Menu)oldInstance;
 902         java.awt.Menu n = (java.awt.Menu)newInstance;
 903         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
 904             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
 905         }
 906     }
 907 }
 908 
 909 // MenuBar
 910 static final class java_awt_MenuBar_PersistenceDelegate extends DefaultPersistenceDelegate {
 911     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 912         super.initialize(type, oldInstance, newInstance, out);
 913         java.awt.MenuBar m = (java.awt.MenuBar)oldInstance;
 914         java.awt.MenuBar n = (java.awt.MenuBar)newInstance;
 915         for (int i = n.getMenuCount(); i < m.getMenuCount(); i++) {
 916             invokeStatement(oldInstance, "add", new Object[]{m.getMenu(i)}, out);
 917         }
 918     }
 919 }
 920 
 921 // List
 922 static final class java_awt_List_PersistenceDelegate extends DefaultPersistenceDelegate {
 923     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
 924         super.initialize(type, oldInstance, newInstance, out);
 925         java.awt.List m = (java.awt.List)oldInstance;
 926         java.awt.List n = (java.awt.List)newInstance;
 927         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
 928             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
 929         }
 930     }
 931 }
 932 
 933 
 934 // LayoutManagers
 935 
 936 // BorderLayout
 937 static final class java_awt_BorderLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
 938     private static final String[] CONSTRAINTS = {
 939             BorderLayout.NORTH,
 940             BorderLayout.SOUTH,
 941             BorderLayout.EAST,
 942             BorderLayout.WEST,
 943             BorderLayout.CENTER,
 944             BorderLayout.PAGE_START,
 945             BorderLayout.PAGE_END,
 946             BorderLayout.LINE_START,
 947             BorderLayout.LINE_END,
 948     };
 949     @Override
 950     protected void initialize(Class<?> type, Object oldInstance,
 951                               Object newInstance, Encoder out) {
 952         super.initialize(type, oldInstance, newInstance, out);
 953         BorderLayout oldLayout = (BorderLayout) oldInstance;
 954         BorderLayout newLayout = (BorderLayout) newInstance;
 955         for (String constraints : CONSTRAINTS) {
 956             Object oldC = oldLayout.getLayoutComponent(constraints);
 957             Object newC = newLayout.getLayoutComponent(constraints);
 958             // Pending, assume any existing elements are OK.
 959             if (oldC != null && newC == null) {
 960                 invokeStatement(oldInstance, "addLayoutComponent",
 961                                 new Object[] { oldC, constraints }, out);
 962             }
 963         }
 964     }
 965 }
 966 
 967 // CardLayout
 968 static final class java_awt_CardLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
 969     protected void initialize(Class<?> type, Object oldInstance,
 970                               Object newInstance, Encoder out) {
 971         super.initialize(type, oldInstance, newInstance, out);
 972         if (getVector(newInstance).isEmpty()) {
 973             for (Object card : getVector(oldInstance)) {
 974                 Object[] args = {MetaData.getPrivateFieldValue(card, "java.awt.CardLayout$Card.name"),
 975                                  MetaData.getPrivateFieldValue(card, "java.awt.CardLayout$Card.comp")};
 976                 invokeStatement(oldInstance, "addLayoutComponent", args, out);
 977             }
 978         }
 979     }
 980     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
 981         return super.mutatesTo(oldInstance, newInstance) && getVector(newInstance).isEmpty();
 982     }
 983     private static Vector<?> getVector(Object instance) {
 984         return (Vector<?>) MetaData.getPrivateFieldValue(instance, "java.awt.CardLayout.vector");
 985     }
 986 }
 987 
 988 // GridBagLayout
 989 static final class java_awt_GridBagLayout_PersistenceDelegate extends DefaultPersistenceDelegate {
 990     protected void initialize(Class<?> type, Object oldInstance,
 991                               Object newInstance, Encoder out) {
 992         super.initialize(type, oldInstance, newInstance, out);
 993         if (getHashtable(newInstance).isEmpty()) {
 994             for (Map.Entry<?,?> entry : getHashtable(oldInstance).entrySet()) {
 995                 Object[] args = {entry.getKey(), entry.getValue()};
 996                 invokeStatement(oldInstance, "addLayoutComponent", args, out);
 997             }
 998         }
 999     }
1000     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
1001         return super.mutatesTo(oldInstance, newInstance) && getHashtable(newInstance).isEmpty();
1002     }
1003     private static Hashtable<?,?> getHashtable(Object instance) {
1004         return (Hashtable<?,?>) MetaData.getPrivateFieldValue(instance, "java.awt.GridBagLayout.comptable");
1005     }
1006 }
1007 
1008 // Swing
1009 
1010 // JFrame (If we do this for Window instead of JFrame, the setVisible call
1011 // will be issued before we have added all the children to the JFrame and
1012 // will appear blank).
1013 static final class javax_swing_JFrame_PersistenceDelegate extends DefaultPersistenceDelegate {
1014     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1015         super.initialize(type, oldInstance, newInstance, out);
1016         java.awt.Window oldC = (java.awt.Window)oldInstance;
1017         java.awt.Window newC = (java.awt.Window)newInstance;
1018         boolean oldV = oldC.isVisible();
1019         boolean newV = newC.isVisible();
1020         if (newV != oldV) {
1021             // false means: don't execute this statement at write time.
1022             boolean executeStatements = out.executeStatements;
1023             out.executeStatements = false;
1024             invokeStatement(oldInstance, "setVisible", new Object[]{Boolean.valueOf(oldV)}, out);
1025             out.executeStatements = executeStatements;
1026         }
1027     }
1028 }
1029 
1030 // Models
1031 
1032 // DefaultListModel
1033 static final class javax_swing_DefaultListModel_PersistenceDelegate extends DefaultPersistenceDelegate {
1034     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1035         // Note, the "size" property will be set here.
1036         super.initialize(type, oldInstance, newInstance, out);
1037         javax.swing.DefaultListModel<?> m = (javax.swing.DefaultListModel<?>)oldInstance;
1038         javax.swing.DefaultListModel<?> n = (javax.swing.DefaultListModel<?>)newInstance;
1039         for (int i = n.getSize(); i < m.getSize(); i++) {
1040             invokeStatement(oldInstance, "add", // Can also use "addElement".
1041                     new Object[]{m.getElementAt(i)}, out);
1042         }
1043     }
1044 }
1045 
1046 // DefaultComboBoxModel
1047 static final class javax_swing_DefaultComboBoxModel_PersistenceDelegate extends DefaultPersistenceDelegate {
1048     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1049         super.initialize(type, oldInstance, newInstance, out);
1050         javax.swing.DefaultComboBoxModel<?> m = (javax.swing.DefaultComboBoxModel<?>)oldInstance;
1051         for (int i = 0; i < m.getSize(); i++) {
1052             invokeStatement(oldInstance, "addElement", new Object[]{m.getElementAt(i)}, out);
1053         }
1054     }
1055 }
1056 
1057 
1058 // DefaultMutableTreeNode
1059 static final class javax_swing_tree_DefaultMutableTreeNode_PersistenceDelegate extends DefaultPersistenceDelegate {
1060     protected void initialize(Class<?> type, Object oldInstance, Object
1061                               newInstance, Encoder out) {
1062         super.initialize(type, oldInstance, newInstance, out);
1063         javax.swing.tree.DefaultMutableTreeNode m =
1064             (javax.swing.tree.DefaultMutableTreeNode)oldInstance;
1065         javax.swing.tree.DefaultMutableTreeNode n =
1066             (javax.swing.tree.DefaultMutableTreeNode)newInstance;
1067         for (int i = n.getChildCount(); i < m.getChildCount(); i++) {
1068             invokeStatement(oldInstance, "add", new
1069                 Object[]{m.getChildAt(i)}, out);
1070         }
1071     }
1072 }
1073 
1074 // ToolTipManager
1075 static final class javax_swing_ToolTipManager_PersistenceDelegate extends PersistenceDelegate {
1076     protected Expression instantiate(Object oldInstance, Encoder out) {
1077         return new Expression(oldInstance, javax.swing.ToolTipManager.class,
1078                               "sharedInstance", new Object[]{});
1079     }
1080 }
1081 
1082 // JTabbedPane
1083 static final class javax_swing_JTabbedPane_PersistenceDelegate extends DefaultPersistenceDelegate {
1084     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1085         super.initialize(type, oldInstance, newInstance, out);
1086         javax.swing.JTabbedPane p = (javax.swing.JTabbedPane)oldInstance;
1087         for (int i = 0; i < p.getTabCount(); i++) {
1088             invokeStatement(oldInstance, "addTab",
1089                                           new Object[]{
1090                                               p.getTitleAt(i),
1091                                               p.getIconAt(i),
1092                                               p.getComponentAt(i)}, out);
1093         }
1094     }
1095 }
1096 
1097 // Box
1098 static final class javax_swing_Box_PersistenceDelegate extends DefaultPersistenceDelegate {
1099     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
1100         return super.mutatesTo(oldInstance, newInstance) && getAxis(oldInstance).equals(getAxis(newInstance));
1101     }
1102 
1103     protected Expression instantiate(Object oldInstance, Encoder out) {
1104         return new Expression(oldInstance, oldInstance.getClass(), "new", new Object[] {getAxis(oldInstance)});
1105     }
1106 
1107     private Integer getAxis(Object object) {
1108         Box box = (Box) object;
1109         return (Integer) MetaData.getPrivateFieldValue(box.getLayout(), "javax.swing.BoxLayout.axis");
1110     }
1111 }
1112 
1113 // JMenu
1114 // Note that we do not need to state the initialiser for
1115 // JMenuItems since the getComponents() method defined in
1116 // Container will return all of the sub menu items that
1117 // need to be added to the menu item.
1118 // Not so for JMenu apparently.
1119 static final class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate {
1120     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1121         super.initialize(type, oldInstance, newInstance, out);
1122         javax.swing.JMenu m = (javax.swing.JMenu)oldInstance;
1123         java.awt.Component[] c = m.getMenuComponents();
1124         for (int i = 0; i < c.length; i++) {
1125             invokeStatement(oldInstance, "add", new Object[]{c[i]}, out);
1126         }
1127     }
1128 }
1129 
1130 /**
1131  * The persistence delegate for {@link MatteBorder}.
1132  * It is impossible to use {@link DefaultPersistenceDelegate}
1133  * because this class does not have writable properties.
1134  *
1135  * @author Sergey A. Malenkov
1136  */
1137 static final class javax_swing_border_MatteBorder_PersistenceDelegate extends PersistenceDelegate {
1138     protected Expression instantiate(Object oldInstance, Encoder out) {
1139         MatteBorder border = (MatteBorder) oldInstance;
1140         Insets insets = border.getBorderInsets();
1141         Object object = border.getTileIcon();
1142         if (object == null) {
1143             object = border.getMatteColor();
1144         }
1145         Object[] args = new Object[] {
1146                 insets.top,
1147                 insets.left,
1148                 insets.bottom,
1149                 insets.right,
1150                 object,
1151         };
1152         return new Expression(border, border.getClass(), "new", args);
1153     }
1154 }
1155 
1156 /* XXX - doens't seem to work. Debug later.
1157 static final class javax_swing_JMenu_PersistenceDelegate extends DefaultPersistenceDelegate {
1158     protected void initialize(Class<?> type, Object oldInstance, Object newInstance, Encoder out) {
1159         super.initialize(type, oldInstance, newInstance, out);
1160         javax.swing.JMenu m = (javax.swing.JMenu)oldInstance;
1161         javax.swing.JMenu n = (javax.swing.JMenu)newInstance;
1162         for (int i = n.getItemCount(); i < m.getItemCount(); i++) {
1163             invokeStatement(oldInstance, "add", new Object[]{m.getItem(i)}, out);
1164         }
1165     }
1166 }
1167 */
1168 
1169 /**
1170  * The persistence delegate for {@link PrintColorUIResource}.
1171  * It is impossible to use {@link DefaultPersistenceDelegate}
1172  * because this class has special rule for serialization:
1173  * it should be converted to {@link ColorUIResource}.
1174  *
1175  * @see PrintColorUIResource#writeReplace
1176  *
1177  * @author Sergey A. Malenkov
1178  */
1179 static final class sun_swing_PrintColorUIResource_PersistenceDelegate extends PersistenceDelegate {
1180     protected boolean mutatesTo(Object oldInstance, Object newInstance) {
1181         return oldInstance.equals(newInstance);
1182     }
1183 
1184     protected Expression instantiate(Object oldInstance, Encoder out) {
1185         Color color = (Color) oldInstance;
1186         Object[] args = new Object[] {color.getRGB()};
1187         return new Expression(color, ColorUIResource.class, "new", args);
1188     }
1189 }
1190 
1191     private static final Map<String,Field> fields = Collections.synchronizedMap(new WeakHashMap<String, Field>());
1192     private static Hashtable<String, PersistenceDelegate> internalPersistenceDelegates = new Hashtable<>();
1193 
1194     private static PersistenceDelegate nullPersistenceDelegate = new NullPersistenceDelegate();
1195     private static PersistenceDelegate enumPersistenceDelegate = new EnumPersistenceDelegate();
1196     private static PersistenceDelegate primitivePersistenceDelegate = new PrimitivePersistenceDelegate();
1197     private static PersistenceDelegate defaultPersistenceDelegate = new DefaultPersistenceDelegate();
1198     private static PersistenceDelegate arrayPersistenceDelegate;
1199     private static PersistenceDelegate proxyPersistenceDelegate;
1200 
1201     static {
1202 
1203         internalPersistenceDelegates.put("java.net.URI",
1204                                          new PrimitivePersistenceDelegate());
1205 
1206         // it is possible because MatteBorder is assignable from MatteBorderUIResource
1207         internalPersistenceDelegates.put("javax.swing.plaf.BorderUIResource$MatteBorderUIResource",
1208                                          new javax_swing_border_MatteBorder_PersistenceDelegate());
1209 
1210         // it is possible because FontUIResource is supported by java_awt_Font_PersistenceDelegate
1211         internalPersistenceDelegates.put("javax.swing.plaf.FontUIResource",
1212                                          new java_awt_Font_PersistenceDelegate());
1213 
1214         // it is possible because KeyStroke is supported by java_awt_AWTKeyStroke_PersistenceDelegate
1215         internalPersistenceDelegates.put("javax.swing.KeyStroke",
1216                                          new java_awt_AWTKeyStroke_PersistenceDelegate());
1217 
1218         internalPersistenceDelegates.put("java.sql.Date", new java_util_Date_PersistenceDelegate());
1219         internalPersistenceDelegates.put("java.sql.Time", new java_util_Date_PersistenceDelegate());
1220     }
1221 
1222     @SuppressWarnings({"rawtypes", "deprecation"})
1223     public static synchronized PersistenceDelegate getPersistenceDelegate(Class type) {
1224         if (type == null) {
1225             return nullPersistenceDelegate;
1226         }
1227         if (Enum.class.isAssignableFrom(type)) {
1228             return enumPersistenceDelegate;
1229         }
1230         if (null != XMLEncoder.primitiveTypeFor(type)) {
1231             return primitivePersistenceDelegate;
1232         }
1233         // The persistence delegate for arrays is non-trivial; instantiate it lazily.
1234         if (type.isArray()) {
1235             if (arrayPersistenceDelegate == null) {
1236                 arrayPersistenceDelegate = new ArrayPersistenceDelegate();
1237             }
1238             return arrayPersistenceDelegate;
1239         }
1240         // Handle proxies lazily for backward compatibility with 1.2.
1241         try {
1242             if (java.lang.reflect.Proxy.isProxyClass(type)) {
1243                 if (proxyPersistenceDelegate == null) {
1244                     proxyPersistenceDelegate = new ProxyPersistenceDelegate();
1245                 }
1246                 return proxyPersistenceDelegate;
1247             }
1248         }
1249         catch(Exception e) {}
1250         // else if (type.getDeclaringClass() != null) {
1251         //     return new DefaultPersistenceDelegate(new String[]{"this$0"});
1252         // }
1253 
1254         String typeName = type.getName();
1255         PersistenceDelegate pd = (PersistenceDelegate)getBeanAttribute(type, "persistenceDelegate");
1256         if (pd == null) {
1257             pd = internalPersistenceDelegates.get(typeName);
1258             if (pd != null) {
1259                 return pd;
1260             }
1261             internalPersistenceDelegates.put(typeName, defaultPersistenceDelegate);
1262             try {
1263                 String name =  type.getName();
1264                 Class<?> c = Class.forName("java.beans.MetaData$" + name.replace('.', '_')
1265                                         + "_PersistenceDelegate");
1266                 pd = (PersistenceDelegate)c.newInstance();
1267                 internalPersistenceDelegates.put(typeName, pd);
1268             }
1269             catch (ClassNotFoundException e) {
1270                 String[] properties = getConstructorProperties(type);
1271                 if (properties != null) {
1272                     pd = new DefaultPersistenceDelegate(properties);
1273                     internalPersistenceDelegates.put(typeName, pd);
1274                 }
1275             }
1276             catch (Exception e) {
1277                 System.err.println("Internal error: " + e);
1278             }
1279         }
1280 
1281         return (pd != null) ? pd : defaultPersistenceDelegate;
1282     }
1283 
1284     private static String[] getConstructorProperties(Class<?> type) {
1285         String[] names = null;
1286         int length = 0;
1287         for (Constructor<?> constructor : type.getConstructors()) {
1288             String[] value = getAnnotationValue(constructor);
1289             if ((value != null) && (length < value.length) && isValid(constructor, value)) {
1290                 names = value;
1291                 length = value.length;
1292             }
1293         }
1294         return names;
1295     }
1296 
1297     private static String[] getAnnotationValue(Constructor<?> constructor) {
1298         ConstructorProperties annotation = constructor.getAnnotation(ConstructorProperties.class);
1299         return (annotation != null)
1300                 ? annotation.value()
1301                 : null;
1302     }
1303 
1304     private static boolean isValid(Constructor<?> constructor, String[] names) {
1305         Class<?>[] parameters = constructor.getParameterTypes();
1306         if (names.length != parameters.length) {
1307             return false;
1308         }
1309         for (String name : names) {
1310             if (name == null) {
1311                 return false;
1312             }
1313         }
1314         return true;
1315     }
1316 
1317     private static Object getBeanAttribute(Class<?> type, String attribute) {
1318         try {
1319             return Introspector.getBeanInfo(type).getBeanDescriptor().getValue(attribute);
1320         } catch (IntrospectionException exception) {
1321             return null;
1322         }
1323     }
1324 
1325     static Object getPrivateFieldValue(Object instance, String name) {
1326         Field field = fields.get(name);
1327         if (field == null) {
1328             int index = name.lastIndexOf('.');
1329             final String className = name.substring(0, index);
1330             final String fieldName = name.substring(1 + index);
1331             field = AccessController.doPrivileged(new PrivilegedAction<Field>() {
1332                 public Field run() {
1333                     try {
1334                         Field field = Class.forName(className).getDeclaredField(fieldName);
1335                         field.setAccessible(true);
1336                         return field;
1337                     }
1338                     catch (ClassNotFoundException exception) {
1339                         throw new IllegalStateException("Could not find class", exception);
1340                     }
1341                     catch (NoSuchFieldException exception) {
1342                         throw new IllegalStateException("Could not find field", exception);
1343                     }
1344                 }
1345             });
1346             fields.put(name, field);
1347         }
1348         try {
1349             return field.get(instance);
1350         }
1351         catch (IllegalAccessException exception) {
1352             throw new IllegalStateException("Could not get value of the field", exception);
1353         }
1354     }
1355 }