KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > beans > PersistenceDelegate


1 /*
2  * @(#)PersistenceDelegate.java 1.11 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package java.beans;
8
9 /**
10  * The PersistenceDelegate class takes the responsibility
11  * for expressing the state of an instance of a given class
12  * in terms of the methods in the class's public API. Instead
13  * of associating the responsibility of persistence with
14  * the class itself as is done, for example, by the
15  * <code>readObject</code> and <code>writeObject</code>
16  * methods used by the <code>ObjectOutputStream</code>, streams like
17  * the <code>XMLEncoder</code> which
18  * use this delegation model can have their behavior controlled
19  * independently of the classes themselves. Normally, the class
20  * is the best place to put such information and conventions
21  * can easily be expressed in this delegation scheme to do just that.
22  * Sometimes however, it is the case that a minor problem
23  * in a single class prevents an entire object graph from
24  * being written and this can leave the application
25  * developer with no recourse but to attempt to shadow
26  * the problematic classes locally or use alternative
27  * persistence techniques. In situations like these, the
28  * delegation model gives a relatively clean mechanism for
29  * the application developer to intervene in all parts of the
30  * serialization process without requiring that modifications
31  * be made to the implementation of classes which are not part
32  * of the application itself.
33  * <p>
34  * In addition to using a delegation model, this persistence
35  * scheme differs from traditional serialization schemes
36  * in requiring an analog of the <code>writeObject</code>
37  * method without a corresponding <code>readObject</code>
38  * method. The <code>writeObject</code> analog encodes each
39  * instance in terms of its public API and there is no need to
40  * define a <code>readObject</code> analog
41  * since the procedure for reading the serialized form
42  * is defined by the semantics of method invocation as laid
43  * out in the Java Language Specification.
44  * Breaking the dependency between <code>writeObject</code>
45  * and <code>readObject</code> implementations, which may
46  * change from version to version, is the key factor
47  * in making the archives produced by this technique immune
48  * to changes in the private implementations of the classes
49  * to which they refer.
50  * <p>
51  * A persistence delegate, may take control of all
52  * aspects of the persistence of an object including:
53  * <ul>
54  * <li>
55  * Deciding whether or not an instance can be mutated
56  * into another instance of the same class.
57  * <li>
58  * Instantiating the object, either by calling a
59  * public constructor or a public factory method.
60  * <li>
61  * Performing the initialization of the object.
62  * </ul>
63  * @see XMLEncoder
64  *
65  * @since 1.4
66  *
67  * @version 1.11 05/05/04
68  * @author Philip Milne
69  */

70
71 public abstract class PersistenceDelegate {
72
73     /**
74      * The <code>writeObject</code> is a single entry point to the persistence
75      * and is used by a <code>Encoder</code> in the traditional
76      * mode of delegation. Although this method is not final,
77      * it should not need to be subclassed under normal circumstances.
78      * <p>
79      * This implementation first checks to see if the stream
80      * has already encountered this object. Next the
81      * <code>mutatesTo</code> method is called to see if
82      * that candidate returned from the stream can
83      * be mutated into an accurate copy of <code>oldInstance</code>.
84      * If it can, the <code>initialize</code> method is called to
85      * perform the initialization. If not, the candidate is removed
86      * from the stream, and the <code>instantiate</code> method
87      * is called to create a new candidate for this object.
88      *
89      * @param oldInstance The instance that will be created by this expression.
90      * @param out The stream to which this expression will be written.
91      * @return An expression whose value is <code>oldInstance</code>.
92      */

93     public void writeObject(Object JavaDoc oldInstance, Encoder JavaDoc out) {
94         Object JavaDoc newInstance = out.get(oldInstance);
95         if (!mutatesTo(oldInstance, newInstance)) {
96             out.remove(oldInstance);
97             out.writeExpression(instantiate(oldInstance, out));
98         }
99         else {
100             initialize(oldInstance.getClass(), oldInstance, newInstance, out);
101         }
102     }
103
104     /**
105      * Returns true if an <em>equivalent</em> copy of <code>oldInstance</code> may be
106      * created by applying a series of statements to <code>newInstance</code>.
107      * In the specification of this method, we mean by equivalent that the modified instance
108      * is indistinguishable from <code>oldInstance</code> in the behavior
109      * of the relevant methods in its public API. [Note: we use the
110      * phrase <em>relevant</em> methods rather than <em>all</em> methods
111      * here only because, to be strictly correct, methods like <code>hashCode</code>
112      * and <code>toString</code> prevent most classes from producing truly
113      * indistinguishable copies of their instances].
114      * <p>
115      * The default behavior returns <code>true</code>
116      * if the classes of the two instances are the same.
117      *
118      * @param oldInstance The instance to be copied.
119      * @param newInstance The instance that is to be modified.
120      * @return True if an equivalent copy of <code>newInstance</code> may be
121      * created by applying a series of mutations to <code>oldInstance</code>.
122      */

123     protected boolean mutatesTo(Object JavaDoc oldInstance, Object JavaDoc newInstance) {
124         return (newInstance != null && oldInstance != null &&
125                 oldInstance.getClass() == newInstance.getClass());
126     }
127
128     /**
129      * Returns an expression whose value is <code>oldInstance</code>.
130      * This method is used to characterize the constructor
131      * or factory method that should be used to create the given object.
132      * For example, the <code>instantiate</code> method of the persistence
133      * delegate for the <code>Field</code> class could be defined as follows:
134      * <pre>
135      * Field f = (Field)oldInstance;
136      * return new Expression(f, f.getDeclaringClass(), "getField", new Object[]{f.getName()});
137      * </pre>
138      * Note that we declare the value of the returned expression so that
139      * the value of the expression (as returned by <code>getValue</code>)
140      * will be identical to <code>oldInstance</code>.
141      *
142      * @param oldInstance The instance that will be created by this expression.
143      * @param out The stream to which this expression will be written.
144      * @return An expression whose value is <code>oldInstance</code>.
145      */

146     protected abstract Expression JavaDoc instantiate(Object JavaDoc oldInstance, Encoder JavaDoc out);
147
148     /**
149      * Produce a series of statements with side effects on <code>newInstance</code>
150      * so that the new instance becomes <em>equivalent</em> to <code>oldInstance</code>.
151      * In the specification of this method, we mean by equivalent that, after the method
152      * returns, the modified instance is indistinguishable from
153      * <code>newInstance</code> in the behavior of all methods in its
154      * public API.
155      * <p>
156      * The implementation typically achieves this goal by producing a series of
157      * "what happened" statements involving the <code>oldInstance</code>
158      * and its publicly available state. These statements are sent
159      * to the output stream using its <code>writeExpression</code>
160      * method which returns an expression involving elements in
161      * a cloned environment simulating the state of an input stream during
162      * reading. Each statement returned will have had all instances
163      * the old environment replaced with objects which exist in the new
164      * one. In particular, references to the target of these statements,
165      * which start out as references to <code>oldInstance</code> are returned
166      * as references to the <code>newInstance</code> instead.
167      * Executing these statements effects an incremental
168      * alignment of the state of the two objects as a series of
169      * modifications to the objects in the new environment.
170      * By the time the initialize method returns it should be impossible
171      * to tell the two instances apart by using their public APIs.
172      * Most importantly, the sequence of steps that were used to make
173      * these objects appear equivalent will have been recorded
174      * by the output stream and will form the actual output when
175      * the stream is flushed.
176      * <p>
177      * The default implementation, calls the <code>initialize</code>
178      * method of the type's superclass.
179      *
180      * @param oldInstance The instance to be copied.
181      * @param newInstance The instance that is to be modified.
182      * @param out The stream to which any initialization statements should be written.
183      */

184     protected void initialize(Class JavaDoc<?> type,
185                   Object JavaDoc oldInstance, Object JavaDoc newInstance,
186                   Encoder JavaDoc out)
187     {
188         Class JavaDoc superType = type.getSuperclass();
189         PersistenceDelegate JavaDoc info = out.getPersistenceDelegate(superType);
190         info.initialize(superType, oldInstance, newInstance, out);
191     }
192 }
193
Popular Tags