author | lana |
Tue, 01 Jun 2010 14:17:38 -0700 | |
changeset 5597 | ab490f66d2cf |
parent 5506 | 202f599c92aa |
child 11120 | f8576c769572 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
5506 | 2 |
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 24 |
*/ |
25 |
package java.beans; |
|
26 |
||
27 |
/** |
|
28 |
* The PersistenceDelegate class takes the responsibility |
|
29 |
* for expressing the state of an instance of a given class |
|
30 |
* in terms of the methods in the class's public API. Instead |
|
31 |
* of associating the responsibility of persistence with |
|
32 |
* the class itself as is done, for example, by the |
|
33 |
* <code>readObject</code> and <code>writeObject</code> |
|
34 |
* methods used by the <code>ObjectOutputStream</code>, streams like |
|
35 |
* the <code>XMLEncoder</code> which |
|
36 |
* use this delegation model can have their behavior controlled |
|
37 |
* independently of the classes themselves. Normally, the class |
|
38 |
* is the best place to put such information and conventions |
|
39 |
* can easily be expressed in this delegation scheme to do just that. |
|
40 |
* Sometimes however, it is the case that a minor problem |
|
41 |
* in a single class prevents an entire object graph from |
|
42 |
* being written and this can leave the application |
|
43 |
* developer with no recourse but to attempt to shadow |
|
44 |
* the problematic classes locally or use alternative |
|
45 |
* persistence techniques. In situations like these, the |
|
46 |
* delegation model gives a relatively clean mechanism for |
|
47 |
* the application developer to intervene in all parts of the |
|
48 |
* serialization process without requiring that modifications |
|
49 |
* be made to the implementation of classes which are not part |
|
50 |
* of the application itself. |
|
51 |
* <p> |
|
52 |
* In addition to using a delegation model, this persistence |
|
53 |
* scheme differs from traditional serialization schemes |
|
54 |
* in requiring an analog of the <code>writeObject</code> |
|
55 |
* method without a corresponding <code>readObject</code> |
|
56 |
* method. The <code>writeObject</code> analog encodes each |
|
57 |
* instance in terms of its public API and there is no need to |
|
58 |
* define a <code>readObject</code> analog |
|
59 |
* since the procedure for reading the serialized form |
|
60 |
* is defined by the semantics of method invocation as laid |
|
61 |
* out in the Java Language Specification. |
|
62 |
* Breaking the dependency between <code>writeObject</code> |
|
63 |
* and <code>readObject</code> implementations, which may |
|
64 |
* change from version to version, is the key factor |
|
65 |
* in making the archives produced by this technique immune |
|
66 |
* to changes in the private implementations of the classes |
|
67 |
* to which they refer. |
|
68 |
* <p> |
|
69 |
* A persistence delegate, may take control of all |
|
70 |
* aspects of the persistence of an object including: |
|
71 |
* <ul> |
|
72 |
* <li> |
|
73 |
* Deciding whether or not an instance can be mutated |
|
74 |
* into another instance of the same class. |
|
75 |
* <li> |
|
76 |
* Instantiating the object, either by calling a |
|
77 |
* public constructor or a public factory method. |
|
78 |
* <li> |
|
79 |
* Performing the initialization of the object. |
|
80 |
* </ul> |
|
81 |
* @see XMLEncoder |
|
82 |
* |
|
83 |
* @since 1.4 |
|
84 |
* |
|
85 |
* @author Philip Milne |
|
86 |
*/ |
|
87 |
||
88 |
public abstract class PersistenceDelegate { |
|
89 |
||
90 |
/** |
|
91 |
* The <code>writeObject</code> is a single entry point to the persistence |
|
92 |
* and is used by a <code>Encoder</code> in the traditional |
|
93 |
* mode of delegation. Although this method is not final, |
|
94 |
* it should not need to be subclassed under normal circumstances. |
|
95 |
* <p> |
|
96 |
* This implementation first checks to see if the stream |
|
97 |
* has already encountered this object. Next the |
|
98 |
* <code>mutatesTo</code> method is called to see if |
|
99 |
* that candidate returned from the stream can |
|
100 |
* be mutated into an accurate copy of <code>oldInstance</code>. |
|
101 |
* If it can, the <code>initialize</code> method is called to |
|
102 |
* perform the initialization. If not, the candidate is removed |
|
103 |
* from the stream, and the <code>instantiate</code> method |
|
104 |
* is called to create a new candidate for this object. |
|
105 |
* |
|
106 |
* @param oldInstance The instance that will be created by this expression. |
|
107 |
* @param out The stream to which this expression will be written. |
|
4849
c83eca4dbb8f
6412286: DOC: LTP: Unspecified NPE in java.beans.DefaultPersistenceDelegate.instantiate method
malenkov
parents:
2
diff
changeset
|
108 |
* |
c83eca4dbb8f
6412286: DOC: LTP: Unspecified NPE in java.beans.DefaultPersistenceDelegate.instantiate method
malenkov
parents:
2
diff
changeset
|
109 |
* @throws NullPointerException if {@code out} is {@code null} |
2 | 110 |
*/ |
111 |
public void writeObject(Object oldInstance, Encoder out) { |
|
112 |
Object newInstance = out.get(oldInstance); |
|
113 |
if (!mutatesTo(oldInstance, newInstance)) { |
|
114 |
out.remove(oldInstance); |
|
115 |
out.writeExpression(instantiate(oldInstance, out)); |
|
116 |
} |
|
117 |
else { |
|
118 |
initialize(oldInstance.getClass(), oldInstance, newInstance, out); |
|
119 |
} |
|
120 |
} |
|
121 |
||
122 |
/** |
|
123 |
* Returns true if an <em>equivalent</em> copy of <code>oldInstance</code> may be |
|
124 |
* created by applying a series of statements to <code>newInstance</code>. |
|
125 |
* In the specification of this method, we mean by equivalent that the modified instance |
|
126 |
* is indistinguishable from <code>oldInstance</code> in the behavior |
|
127 |
* of the relevant methods in its public API. [Note: we use the |
|
128 |
* phrase <em>relevant</em> methods rather than <em>all</em> methods |
|
129 |
* here only because, to be strictly correct, methods like <code>hashCode</code> |
|
130 |
* and <code>toString</code> prevent most classes from producing truly |
|
131 |
* indistinguishable copies of their instances]. |
|
132 |
* <p> |
|
133 |
* The default behavior returns <code>true</code> |
|
134 |
* if the classes of the two instances are the same. |
|
135 |
* |
|
136 |
* @param oldInstance The instance to be copied. |
|
137 |
* @param newInstance The instance that is to be modified. |
|
138 |
* @return True if an equivalent copy of <code>newInstance</code> may be |
|
139 |
* created by applying a series of mutations to <code>oldInstance</code>. |
|
140 |
*/ |
|
141 |
protected boolean mutatesTo(Object oldInstance, Object newInstance) { |
|
142 |
return (newInstance != null && oldInstance != null && |
|
143 |
oldInstance.getClass() == newInstance.getClass()); |
|
144 |
} |
|
145 |
||
146 |
/** |
|
147 |
* Returns an expression whose value is <code>oldInstance</code>. |
|
148 |
* This method is used to characterize the constructor |
|
149 |
* or factory method that should be used to create the given object. |
|
150 |
* For example, the <code>instantiate</code> method of the persistence |
|
151 |
* delegate for the <code>Field</code> class could be defined as follows: |
|
152 |
* <pre> |
|
153 |
* Field f = (Field)oldInstance; |
|
154 |
* return new Expression(f, f.getDeclaringClass(), "getField", new Object[]{f.getName()}); |
|
155 |
* </pre> |
|
156 |
* Note that we declare the value of the returned expression so that |
|
157 |
* the value of the expression (as returned by <code>getValue</code>) |
|
158 |
* will be identical to <code>oldInstance</code>. |
|
159 |
* |
|
160 |
* @param oldInstance The instance that will be created by this expression. |
|
161 |
* @param out The stream to which this expression will be written. |
|
162 |
* @return An expression whose value is <code>oldInstance</code>. |
|
4849
c83eca4dbb8f
6412286: DOC: LTP: Unspecified NPE in java.beans.DefaultPersistenceDelegate.instantiate method
malenkov
parents:
2
diff
changeset
|
163 |
* |
c83eca4dbb8f
6412286: DOC: LTP: Unspecified NPE in java.beans.DefaultPersistenceDelegate.instantiate method
malenkov
parents:
2
diff
changeset
|
164 |
* @throws NullPointerException if {@code out} is {@code null} |
2 | 165 |
*/ |
166 |
protected abstract Expression instantiate(Object oldInstance, Encoder out); |
|
167 |
||
168 |
/** |
|
169 |
* Produce a series of statements with side effects on <code>newInstance</code> |
|
170 |
* so that the new instance becomes <em>equivalent</em> to <code>oldInstance</code>. |
|
171 |
* In the specification of this method, we mean by equivalent that, after the method |
|
172 |
* returns, the modified instance is indistinguishable from |
|
173 |
* <code>newInstance</code> in the behavior of all methods in its |
|
174 |
* public API. |
|
175 |
* <p> |
|
176 |
* The implementation typically achieves this goal by producing a series of |
|
177 |
* "what happened" statements involving the <code>oldInstance</code> |
|
178 |
* and its publicly available state. These statements are sent |
|
179 |
* to the output stream using its <code>writeExpression</code> |
|
180 |
* method which returns an expression involving elements in |
|
181 |
* a cloned environment simulating the state of an input stream during |
|
182 |
* reading. Each statement returned will have had all instances |
|
183 |
* the old environment replaced with objects which exist in the new |
|
184 |
* one. In particular, references to the target of these statements, |
|
185 |
* which start out as references to <code>oldInstance</code> are returned |
|
186 |
* as references to the <code>newInstance</code> instead. |
|
187 |
* Executing these statements effects an incremental |
|
188 |
* alignment of the state of the two objects as a series of |
|
189 |
* modifications to the objects in the new environment. |
|
190 |
* By the time the initialize method returns it should be impossible |
|
191 |
* to tell the two instances apart by using their public APIs. |
|
192 |
* Most importantly, the sequence of steps that were used to make |
|
193 |
* these objects appear equivalent will have been recorded |
|
194 |
* by the output stream and will form the actual output when |
|
195 |
* the stream is flushed. |
|
196 |
* <p> |
|
197 |
* The default implementation, calls the <code>initialize</code> |
|
198 |
* method of the type's superclass. |
|
199 |
* |
|
200 |
* @param oldInstance The instance to be copied. |
|
201 |
* @param newInstance The instance that is to be modified. |
|
202 |
* @param out The stream to which any initialization statements should be written. |
|
4849
c83eca4dbb8f
6412286: DOC: LTP: Unspecified NPE in java.beans.DefaultPersistenceDelegate.instantiate method
malenkov
parents:
2
diff
changeset
|
203 |
* |
c83eca4dbb8f
6412286: DOC: LTP: Unspecified NPE in java.beans.DefaultPersistenceDelegate.instantiate method
malenkov
parents:
2
diff
changeset
|
204 |
* @throws NullPointerException if {@code out} is {@code null} |
2 | 205 |
*/ |
206 |
protected void initialize(Class<?> type, |
|
207 |
Object oldInstance, Object newInstance, |
|
208 |
Encoder out) |
|
209 |
{ |
|
210 |
Class superType = type.getSuperclass(); |
|
211 |
PersistenceDelegate info = out.getPersistenceDelegate(superType); |
|
212 |
info.initialize(superType, oldInstance, newInstance, out); |
|
213 |
} |
|
214 |
} |