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 */
25package java.beans;
26
27import java.util.*;
28import java.lang.reflect.*;
29import java.util.Objects;
30import 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
60public 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}