MetaData.java revision 15728:64d3579d3ebc
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 */
25package java.beans;
26
27import com.sun.beans.finder.PrimitiveWrapperMap;
28
29import java.awt.AWTKeyStroke;
30import java.awt.BorderLayout;
31import java.awt.Dimension;
32import java.awt.Color;
33import java.awt.Font;
34import java.awt.GridBagConstraints;
35import java.awt.Insets;
36import java.awt.Point;
37import java.awt.Rectangle;
38import java.awt.event.KeyEvent;
39import java.awt.font.TextAttribute;
40
41import java.lang.reflect.Array;
42import java.lang.reflect.Constructor;
43import java.lang.reflect.Field;
44import java.lang.reflect.Method;
45import java.lang.reflect.Modifier;
46import java.lang.reflect.InvocationTargetException;
47
48import java.security.AccessController;
49import java.security.PrivilegedAction;
50
51import java.util.*;
52
53import javax.swing.Box;
54import javax.swing.JLayeredPane;
55import javax.swing.border.MatteBorder;
56import javax.swing.plaf.ColorUIResource;
57
58import sun.swing.PrintColorUIResource;
59
60import 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 */
73class MetaData {
74
75static 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 */
92static 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
103static 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
114static 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
155static 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
190static 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
199static 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
233static 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
248static 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 */
272static 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 */
295static 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/*
341The Hashtable and AbstractMap classes have no common ancestor yet may
342be handled with a single persistence delegate: one which uses the methods
343of the Map insterface exclusively. Attatching the persistence delegates
344to the interfaces themselves is fraught however since, in the case of
345the Map, both the AbstractMap and HashMap classes are declared to
346implement the Map interface, leaving the obvious implementation prone
347to repeating their initialization. These issues and questions around
348the ordering of delegates attached to interfaces have lead us to
349ignore any delegates attached to interfaces and force all persistence
350delegates 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 */
359private 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
516static 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
531static 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
566static 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
604static final class java_util_AbstractCollection_PersistenceDelegate extends java_util_Collection_PersistenceDelegate {}
605static final class java_util_AbstractList_PersistenceDelegate extends java_util_List_PersistenceDelegate {}
606static final class java_util_AbstractMap_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
607static final class java_util_Hashtable_PersistenceDelegate extends java_util_Map_PersistenceDelegate {}
608
609
610// Beans
611static 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 */
622static 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 */
646static 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 */
715static 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
756static 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
785static final class java_awt_SystemColor_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
786
787// TextAttribute
788static final class java_awt_font_TextAttribute_PersistenceDelegate extends StaticFieldsPersistenceDelegate {}
789
790// MenuShortcut
791static 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
804static 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
851static 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
886static 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
898static 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
910static 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
922static 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
937static 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
968static 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
989static 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).
1013static 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
1033static 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
1047static 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
1059static 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
1075static 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
1083static 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
1098static 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.
1119static 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 */
1137static 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.
1157static 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 */
1179static 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")
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}
1356