Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/beans/PersistenceDelegate.java
38829 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package java.beans;2526/**27* The PersistenceDelegate class takes the responsibility28* for expressing the state of an instance of a given class29* in terms of the methods in the class's public API. Instead30* of associating the responsibility of persistence with31* the class itself as is done, for example, by the32* <code>readObject</code> and <code>writeObject</code>33* methods used by the <code>ObjectOutputStream</code>, streams like34* the <code>XMLEncoder</code> which35* use this delegation model can have their behavior controlled36* independently of the classes themselves. Normally, the class37* is the best place to put such information and conventions38* can easily be expressed in this delegation scheme to do just that.39* Sometimes however, it is the case that a minor problem40* in a single class prevents an entire object graph from41* being written and this can leave the application42* developer with no recourse but to attempt to shadow43* the problematic classes locally or use alternative44* persistence techniques. In situations like these, the45* delegation model gives a relatively clean mechanism for46* the application developer to intervene in all parts of the47* serialization process without requiring that modifications48* be made to the implementation of classes which are not part49* of the application itself.50* <p>51* In addition to using a delegation model, this persistence52* scheme differs from traditional serialization schemes53* in requiring an analog of the <code>writeObject</code>54* method without a corresponding <code>readObject</code>55* method. The <code>writeObject</code> analog encodes each56* instance in terms of its public API and there is no need to57* define a <code>readObject</code> analog58* since the procedure for reading the serialized form59* is defined by the semantics of method invocation as laid60* out in the Java Language Specification.61* Breaking the dependency between <code>writeObject</code>62* and <code>readObject</code> implementations, which may63* change from version to version, is the key factor64* in making the archives produced by this technique immune65* to changes in the private implementations of the classes66* to which they refer.67* <p>68* A persistence delegate, may take control of all69* aspects of the persistence of an object including:70* <ul>71* <li>72* Deciding whether or not an instance can be mutated73* into another instance of the same class.74* <li>75* Instantiating the object, either by calling a76* public constructor or a public factory method.77* <li>78* Performing the initialization of the object.79* </ul>80* @see XMLEncoder81*82* @since 1.483*84* @author Philip Milne85*/8687public abstract class PersistenceDelegate {8889/**90* The <code>writeObject</code> is a single entry point to the persistence91* and is used by a <code>Encoder</code> in the traditional92* mode of delegation. Although this method is not final,93* it should not need to be subclassed under normal circumstances.94* <p>95* This implementation first checks to see if the stream96* has already encountered this object. Next the97* <code>mutatesTo</code> method is called to see if98* that candidate returned from the stream can99* be mutated into an accurate copy of <code>oldInstance</code>.100* If it can, the <code>initialize</code> method is called to101* perform the initialization. If not, the candidate is removed102* from the stream, and the <code>instantiate</code> method103* is called to create a new candidate for this object.104*105* @param oldInstance The instance that will be created by this expression.106* @param out The stream to which this expression will be written.107*108* @throws NullPointerException if {@code out} is {@code null}109*/110public void writeObject(Object oldInstance, Encoder out) {111Object newInstance = out.get(oldInstance);112if (!mutatesTo(oldInstance, newInstance)) {113out.remove(oldInstance);114out.writeExpression(instantiate(oldInstance, out));115}116else {117initialize(oldInstance.getClass(), oldInstance, newInstance, out);118}119}120121/**122* Returns true if an <em>equivalent</em> copy of <code>oldInstance</code> may be123* created by applying a series of statements to <code>newInstance</code>.124* In the specification of this method, we mean by equivalent that the modified instance125* is indistinguishable from <code>oldInstance</code> in the behavior126* of the relevant methods in its public API. [Note: we use the127* phrase <em>relevant</em> methods rather than <em>all</em> methods128* here only because, to be strictly correct, methods like <code>hashCode</code>129* and <code>toString</code> prevent most classes from producing truly130* indistinguishable copies of their instances].131* <p>132* The default behavior returns <code>true</code>133* if the classes of the two instances are the same.134*135* @param oldInstance The instance to be copied.136* @param newInstance The instance that is to be modified.137* @return True if an equivalent copy of <code>newInstance</code> may be138* created by applying a series of mutations to <code>oldInstance</code>.139*/140protected boolean mutatesTo(Object oldInstance, Object newInstance) {141return (newInstance != null && oldInstance != null &&142oldInstance.getClass() == newInstance.getClass());143}144145/**146* Returns an expression whose value is <code>oldInstance</code>.147* This method is used to characterize the constructor148* or factory method that should be used to create the given object.149* For example, the <code>instantiate</code> method of the persistence150* delegate for the <code>Field</code> class could be defined as follows:151* <pre>152* Field f = (Field)oldInstance;153* return new Expression(f, f.getDeclaringClass(), "getField", new Object[]{f.getName()});154* </pre>155* Note that we declare the value of the returned expression so that156* the value of the expression (as returned by <code>getValue</code>)157* will be identical to <code>oldInstance</code>.158*159* @param oldInstance The instance that will be created by this expression.160* @param out The stream to which this expression will be written.161* @return An expression whose value is <code>oldInstance</code>.162*163* @throws NullPointerException if {@code out} is {@code null}164* and this value is used in the method165*/166protected abstract Expression instantiate(Object oldInstance, Encoder out);167168/**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 method172* returns, the modified instance is indistinguishable from173* <code>newInstance</code> in the behavior of all methods in its174* public API.175* <p>176* The implementation typically achieves this goal by producing a series of177* "what happened" statements involving the <code>oldInstance</code>178* and its publicly available state. These statements are sent179* to the output stream using its <code>writeExpression</code>180* method which returns an expression involving elements in181* a cloned environment simulating the state of an input stream during182* reading. Each statement returned will have had all instances183* the old environment replaced with objects which exist in the new184* one. In particular, references to the target of these statements,185* which start out as references to <code>oldInstance</code> are returned186* as references to the <code>newInstance</code> instead.187* Executing these statements effects an incremental188* alignment of the state of the two objects as a series of189* modifications to the objects in the new environment.190* By the time the initialize method returns it should be impossible191* to tell the two instances apart by using their public APIs.192* Most importantly, the sequence of steps that were used to make193* these objects appear equivalent will have been recorded194* by the output stream and will form the actual output when195* 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 type the type of the instances201* @param oldInstance The instance to be copied.202* @param newInstance The instance that is to be modified.203* @param out The stream to which any initialization statements should be written.204*205* @throws NullPointerException if {@code out} is {@code null}206*/207protected void initialize(Class<?> type,208Object oldInstance, Object newInstance,209Encoder out)210{211Class<?> superType = type.getSuperclass();212PersistenceDelegate info = out.getPersistenceDelegate(superType);213info.initialize(superType, oldInstance, newInstance, out);214}215}216217218