History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/beans/DefaultPersistenceDelegate.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/beans/DefaultPersistenceDelegate.java+414−0
@@ -0,0 +1,414 @@
1+/*
2+ * Copyright (c) 2000, 2011, 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 java.util.*;
28+import java.lang.reflect.*;
29+import java.util.Objects;
30+import sun.reflect.misc.*;
31+
32+
33+/**
34+ * The <code>DefaultPersistenceDelegate</code> is a concrete implementation of
35+ * the abstract <code>PersistenceDelegate</code> class and
36+ * is the delegate used by default for classes about
37+ * which no information is available. The <code>DefaultPersistenceDelegate</code>
38+ * provides, version resilient, public API-based persistence for
39+ * classes that follow the JavaBeans&trade; conventions without any class specific
40+ * configuration.
41+ * <p>
42+ * The key assumptions are that the class has a nullary constructor
43+ * and that its state is accurately represented by matching pairs
44+ * of "setter" and "getter" methods in the order they are returned
45+ * by the Introspector.
46+ * In addition to providing code-free persistence for JavaBeans,
47+ * the <code>DefaultPersistenceDelegate</code> provides a convenient means
48+ * to effect persistent storage for classes that have a constructor
49+ * that, while not nullary, simply requires some property values
50+ * as arguments.
51+ *
52+ * @see #DefaultPersistenceDelegate(String[])
53+ * @see java.beans.Introspector
54+ *
55+ * @since 1.4
56+ *
57+ * @author Philip Milne
58+ */
59+
60+public class DefaultPersistenceDelegate extends PersistenceDelegate {
61+ private String[] constructor;
62+ private Boolean definesEquals;
63+
64+ /**
65+ * Creates a persistence delegate for a class with a nullary constructor.
66+ *
67+ * @see #DefaultPersistenceDelegate(java.lang.String[])
68+ */
69+ public DefaultPersistenceDelegate() {
70+ this(new String[0]);
71+ }
72+
73+ /**
74+ * Creates a default persistence delegate for a class with a
75+ * constructor whose arguments are the values of the property
76+ * names as specified by <code>constructorPropertyNames</code>.
77+ * The constructor arguments are created by
78+ * evaluating the property names in the order they are supplied.
79+ * To use this class to specify a single preferred constructor for use
80+ * in the serialization of a particular type, we state the
81+ * names of the properties that make up the constructor's
82+ * arguments. For example, the <code>Font</code> class which
83+ * does not define a nullary constructor can be handled
84+ * with the following persistence delegate:
85+ *
86+ * <pre>
87+ * new DefaultPersistenceDelegate(new String[]{"name", "style", "size"});
88+ * </pre>
89+ *
90+ * @param constructorPropertyNames The property names for the arguments of this constructor.
91+ *
92+ * @see #instantiate
93+ */
94+ public DefaultPersistenceDelegate(String[] constructorPropertyNames) {
95+ this.constructor = constructorPropertyNames;
96+ }
97+
98+ private static boolean definesEquals(Class type) {
99+ try {
100+ return type == type.getMethod("equals", Object.class).getDeclaringClass();
101+ }
102+ catch(NoSuchMethodException e) {
103+ return false;
104+ }
105+ }
106+
107+ private boolean definesEquals(Object instance) {
108+ if (definesEquals != null) {
109+ return (definesEquals == Boolean.TRUE);
110+ }
111+ else {
112+ boolean result = definesEquals(instance.getClass());
113+ definesEquals = result ? Boolean.TRUE : Boolean.FALSE;
114+ return result;
115+ }
116+ }
117+
118+ /**
119+ * If the number of arguments in the specified constructor is non-zero and
120+ * the class of <code>oldInstance</code> explicitly declares an "equals" method
121+ * this method returns the value of <code>oldInstance.equals(newInstance)</code>.
122+ * Otherwise, this method uses the superclass's definition which returns true if the
123+ * classes of the two instances are equal.
124+ *
125+ * @param oldInstance The instance to be copied.
126+ * @param newInstance The instance that is to be modified.
127+ * @return True if an equivalent copy of <code>newInstance</code> may be
128+ * created by applying a series of mutations to <code>oldInstance</code>.
129+ *
130+ * @see #DefaultPersistenceDelegate(String[])
131+ */
132+ protected boolean mutatesTo(Object oldInstance, Object newInstance) {
133+ // Assume the instance is either mutable or a singleton
134+ // if it has a nullary constructor.
135+ return (constructor.length == 0) || !definesEquals(oldInstance) ?
136+ super.mutatesTo(oldInstance, newInstance) :
137+ oldInstance.equals(newInstance);
138+ }
139+
140+ /**
141+ * This default implementation of the <code>instantiate</code> method returns
142+ * an expression containing the predefined method name "new" which denotes a
143+ * call to a constructor with the arguments as specified in
144+ * the <code>DefaultPersistenceDelegate</code>'s constructor.
145+ *
146+ * @param oldInstance The instance to be instantiated.
147+ * @param out The code output stream.
148+ * @return An expression whose value is <code>oldInstance</code>.
149+ *
150+ * @throws NullPointerException if {@code out} is {@code null}
151+ *
152+ * @see #DefaultPersistenceDelegate(String[])
153+ */
154+ protected Expression instantiate(Object oldInstance, Encoder out) {
155+ int nArgs = constructor.length;
156+ Class type = oldInstance.getClass();
157+ Object[] constructorArgs = new Object[nArgs];
158+ for(int i = 0; i < nArgs; i++) {
159+ try {
160+ Method method = findMethod(type, this.constructor[i]);
161+ constructorArgs[i] = MethodUtil.invoke(method, oldInstance, new Object[0]);
162+ }
163+ catch (Exception e) {
164+ out.getExceptionListener().exceptionThrown(e);
165+ }
166+ }
167+ return new Expression(oldInstance, oldInstance.getClass(), "new", constructorArgs);
168+ }
169+
170+ private Method findMethod(Class type, String property) {
171+ if (property == null) {
172+ throw new IllegalArgumentException("Property name is null");
173+ }
174+ PropertyDescriptor pd = getPropertyDescriptor(type, property);
175+ if (pd == null) {
176+ throw new IllegalStateException("Could not find property by the name " + property);
177+ }
178+ Method method = pd.getReadMethod();
179+ if (method == null) {
180+ throw new IllegalStateException("Could not find getter for the property " + property);
181+ }
182+ return method;
183+ }
184+
185+ private void doProperty(Class type, PropertyDescriptor pd, Object oldInstance, Object newInstance, Encoder out) throws Exception {
186+ Method getter = pd.getReadMethod();
187+ Method setter = pd.getWriteMethod();
188+
189+ if (getter != null && setter != null) {
190+ Expression oldGetExp = new Expression(oldInstance, getter.getName(), new Object[]{});
191+ Expression newGetExp = new Expression(newInstance, getter.getName(), new Object[]{});
192+ Object oldValue = oldGetExp.getValue();
193+ Object newValue = newGetExp.getValue();
194+ out.writeExpression(oldGetExp);
195+ if (!Objects.equals(newValue, out.get(oldValue))) {
196+ // Search for a static constant with this value;
197+ Object e = (Object[])pd.getValue("enumerationValues");
198+ if (e instanceof Object[] && Array.getLength(e) % 3 == 0) {
199+ Object[] a = (Object[])e;
200+ for(int i = 0; i < a.length; i = i + 3) {
201+ try {
202+ Field f = type.getField((String)a[i]);
203+ if (f.get(null).equals(oldValue)) {
204+ out.remove(oldValue);
205+ out.writeExpression(new Expression(oldValue, f, "get", new Object[]{null}));
206+ }
207+ }
208+ catch (Exception ex) {}
209+ }
210+ }
211+ invokeStatement(oldInstance, setter.getName(), new Object[]{oldValue}, out);
212+ }
213+ }
214+ }
215+
216+ static void invokeStatement(Object instance, String methodName, Object[] args, Encoder out) {
217+ out.writeStatement(new Statement(instance, methodName, args));
218+ }
219+
220+ // Write out the properties of this instance.
221+ private void initBean(Class type, Object oldInstance, Object newInstance, Encoder out) {
222+ for (Field field : type.getFields()) {
223+ int mod = field.getModifiers();
224+ if (Modifier.isFinal(mod) || Modifier.isStatic(mod) || Modifier.isTransient(mod)) {
225+ continue;
226+ }
227+ try {
228+ Expression oldGetExp = new Expression(field, "get", new Object[] { oldInstance });
229+ Expression newGetExp = new Expression(field, "get", new Object[] { newInstance });
230+ Object oldValue = oldGetExp.getValue();
231+ Object newValue = newGetExp.getValue();
232+ out.writeExpression(oldGetExp);
233+ if (!Objects.equals(newValue, out.get(oldValue))) {
234+ out.writeStatement(new Statement(field, "set", new Object[] { oldInstance, oldValue }));
235+ }
236+ }
237+ catch (Exception exception) {
238+ out.getExceptionListener().exceptionThrown(exception);
239+ }
240+ }
241+ BeanInfo info;
242+ try {
243+ info = Introspector.getBeanInfo(type);
244+ } catch (IntrospectionException exception) {
245+ return;
246+ }
247+ // Properties
248+ for (PropertyDescriptor d : info.getPropertyDescriptors()) {
249+ if (d.isTransient()) {
250+ continue;
251+ }
252+ try {
253+ doProperty(type, d, oldInstance, newInstance, out);
254+ }
255+ catch (Exception e) {
256+ out.getExceptionListener().exceptionThrown(e);
257+ }
258+ }
259+
260+ // Listeners
261+ /*
262+ Pending(milne). There is a general problem with the archival of
263+ listeners which is unresolved as of 1.4. Many of the methods
264+ which install one object inside another (typically "add" methods
265+ or setters) automatically install a listener on the "child" object
266+ so that its "parent" may respond to changes that are made to it.
267+ For example the JTable:setModel() method automatically adds a
268+ TableModelListener (the JTable itself in this case) to the supplied
269+ table model.
270+
271+ We do not need to explictly add these listeners to the model in an
272+ archive as they will be added automatically by, in the above case,
273+ the JTable's "setModel" method. In some cases, we must specifically
274+ avoid trying to do this since the listener may be an inner class
275+ that cannot be instantiated using public API.
276+
277+ No general mechanism currently
278+ exists for differentiating between these kind of listeners and
279+ those which were added explicitly by the user. A mechanism must
280+ be created to provide a general means to differentiate these
281+ special cases so as to provide reliable persistence of listeners
282+ for the general case.
283+ */
284+ if (!java.awt.Component.class.isAssignableFrom(type)) {
285+ return; // Just handle the listeners of Components for now.
286+ }
287+ for (EventSetDescriptor d : info.getEventSetDescriptors()) {
288+ if (d.isTransient()) {
289+ continue;
290+ }
291+ Class listenerType = d.getListenerType();
292+
293+
294+ // The ComponentListener is added automatically, when
295+ // Contatiner:add is called on the parent.
296+ if (listenerType == java.awt.event.ComponentListener.class) {
297+ continue;
298+ }
299+
300+ // JMenuItems have a change listener added to them in
301+ // their "add" methods to enable accessibility support -
302+ // see the add method in JMenuItem for details. We cannot
303+ // instantiate this instance as it is a private inner class
304+ // and do not need to do this anyway since it will be created
305+ // and installed by the "add" method. Special case this for now,
306+ // ignoring all change listeners on JMenuItems.
307+ if (listenerType == javax.swing.event.ChangeListener.class &&
308+ type == javax.swing.JMenuItem.class) {
309+ continue;
310+ }
311+
312+ EventListener[] oldL = new EventListener[0];
313+ EventListener[] newL = new EventListener[0];
314+ try {
315+ Method m = d.getGetListenerMethod();
316+ oldL = (EventListener[])MethodUtil.invoke(m, oldInstance, new Object[]{});
317+ newL = (EventListener[])MethodUtil.invoke(m, newInstance, new Object[]{});
318+ }
319+ catch (Exception e2) {
320+ try {
321+ Method m = type.getMethod("getListeners", new Class[]{Class.class});
322+ oldL = (EventListener[])MethodUtil.invoke(m, oldInstance, new Object[]{listenerType});
323+ newL = (EventListener[])MethodUtil.invoke(m, newInstance, new Object[]{listenerType});
324+ }
325+ catch (Exception e3) {
326+ return;
327+ }
328+ }
329+
330+ // Asssume the listeners are in the same order and that there are no gaps.
331+ // Eventually, this may need to do true differencing.
332+ String addListenerMethodName = d.getAddListenerMethod().getName();
333+ for (int i = newL.length; i < oldL.length; i++) {
334+ // System.out.println("Adding listener: " + addListenerMethodName + oldL[i]);
335+ invokeStatement(oldInstance, addListenerMethodName, new Object[]{oldL[i]}, out);
336+ }
337+
338+ String removeListenerMethodName = d.getRemoveListenerMethod().getName();
339+ for (int i = oldL.length; i < newL.length; i++) {
340+ invokeStatement(oldInstance, removeListenerMethodName, new Object[]{newL[i]}, out);
341+ }
342+ }
343+ }
344+
345+ /**
346+ * This default implementation of the <code>initialize</code> method assumes
347+ * all state held in objects of this type is exposed via the
348+ * matching pairs of "setter" and "getter" methods in the order
349+ * they are returned by the Introspector. If a property descriptor
350+ * defines a "transient" attribute with a value equal to
351+ * <code>Boolean.TRUE</code> the property is ignored by this
352+ * default implementation. Note that this use of the word
353+ * "transient" is quite independent of the field modifier
354+ * that is used by the <code>ObjectOutputStream</code>.
355+ * <p>
356+ * For each non-transient property, an expression is created
357+ * in which the nullary "getter" method is applied
358+ * to the <code>oldInstance</code>. The value of this
359+ * expression is the value of the property in the instance that is
360+ * being serialized. If the value of this expression
361+ * in the cloned environment <code>mutatesTo</code> the
362+ * target value, the new value is initialized to make it
363+ * equivalent to the old value. In this case, because
364+ * the property value has not changed there is no need to
365+ * call the corresponding "setter" method and no statement
366+ * is emitted. If not however, the expression for this value
367+ * is replaced with another expression (normally a constructor)
368+ * and the corresponding "setter" method is called to install
369+ * the new property value in the object. This scheme removes
370+ * default information from the output produced by streams
371+ * using this delegate.
372+ * <p>
373+ * In passing these statements to the output stream, where they
374+ * will be executed, side effects are made to the <code>newInstance</code>.
375+ * In most cases this allows the problem of properties
376+ * whose values depend on each other to actually help the
377+ * serialization process by making the number of statements
378+ * that need to be written to the output smaller. In general,
379+ * the problem of handling interdependent properties is reduced to
380+ * that of finding an order for the properties in
381+ * a class such that no property value depends on the value of
382+ * a subsequent property.
383+ *
384+ * @param oldInstance The instance to be copied.
385+ * @param newInstance The instance that is to be modified.
386+ * @param out The stream to which any initialization statements should be written.
387+ *
388+ * @throws NullPointerException if {@code out} is {@code null}
389+ *
390+ * @see java.beans.Introspector#getBeanInfo
391+ * @see java.beans.PropertyDescriptor
392+ */
393+ protected void initialize(Class<?> type,
394+ Object oldInstance, Object newInstance,
395+ Encoder out)
396+ {
397+ // System.out.println("DefulatPD:initialize" + type);
398+ super.initialize(type, oldInstance, newInstance, out);
399+ if (oldInstance.getClass() == type) { // !type.isInterface()) {
400+ initBean(type, oldInstance, newInstance, out);
401+ }
402+ }
403+
404+ private static PropertyDescriptor getPropertyDescriptor(Class type, String property) {
405+ try {
406+ for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
407+ if (property.equals(pd.getName()))
408+ return pd;
409+ }
410+ } catch (IntrospectionException exception) {
411+ }
412+ return null;
413+ }
414+}