Path: blob/master/src/java.desktop/share/classes/java/beans/PersistenceDelegate.java
41152 views
/*1* Copyright (c) 2000, 2020, 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} and {@code writeObject}33* methods used by the {@code ObjectOutputStream}, streams like34* the {@code XMLEncoder} 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}54* method without a corresponding {@code readObject}55* method. The {@code writeObject} analog encodes each56* instance in terms of its public API and there is no need to57* define a {@code readObject} 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}62* and {@code readObject} 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* Constructs a {@code PersistenceDelegate}.91*/92protected PersistenceDelegate() {}9394/**95* The {@code writeObject} is a single entry point to the persistence96* and is used by an {@code Encoder} in the traditional97* mode of delegation. Although this method is not final,98* it should not need to be subclassed under normal circumstances.99* <p>100* This implementation first checks to see if the stream101* has already encountered this object. Next the102* {@code mutatesTo} method is called to see if103* that candidate returned from the stream can104* be mutated into an accurate copy of {@code oldInstance}.105* If it can, the {@code initialize} method is called to106* perform the initialization. If not, the candidate is removed107* from the stream, and the {@code instantiate} method108* is called to create a new candidate for this object.109*110* @param oldInstance The instance that will be created by this expression.111* @param out The stream to which this expression will be written.112*113* @throws NullPointerException if {@code out} is {@code null}114*/115public void writeObject(Object oldInstance, Encoder out) {116Object newInstance = out.get(oldInstance);117if (!mutatesTo(oldInstance, newInstance)) {118out.remove(oldInstance);119out.writeExpression(instantiate(oldInstance, out));120}121else {122initialize(oldInstance.getClass(), oldInstance, newInstance, out);123}124}125126/**127* Returns true if an <em>equivalent</em> copy of {@code oldInstance} may be128* created by applying a series of statements to {@code newInstance}.129* In the specification of this method, we mean by equivalent that the modified instance130* is indistinguishable from {@code oldInstance} in the behavior131* of the relevant methods in its public API. [Note: we use the132* phrase <em>relevant</em> methods rather than <em>all</em> methods133* here only because, to be strictly correct, methods like {@code hashCode}134* and {@code toString} prevent most classes from producing truly135* indistinguishable copies of their instances].136* <p>137* The default behavior returns {@code true}138* if the classes of the two instances are the same.139*140* @param oldInstance The instance to be copied.141* @param newInstance The instance that is to be modified.142* @return True if an equivalent copy of {@code newInstance} may be143* created by applying a series of mutations to {@code oldInstance}.144*/145protected boolean mutatesTo(Object oldInstance, Object newInstance) {146return (newInstance != null && oldInstance != null &&147oldInstance.getClass() == newInstance.getClass());148}149150/**151* Returns an expression whose value is {@code oldInstance}.152* This method is used to characterize the constructor153* or factory method that should be used to create the given object.154* For example, the {@code instantiate} method of the persistence155* delegate for the {@code Field} class could be defined as follows:156* <pre>157* Field f = (Field)oldInstance;158* return new Expression(f, f.getDeclaringClass(), "getField", new Object[]{f.getName()});159* </pre>160* Note that we declare the value of the returned expression so that161* the value of the expression (as returned by {@code getValue})162* will be identical to {@code oldInstance}.163*164* @param oldInstance The instance that will be created by this expression.165* @param out The stream to which this expression will be written.166* @return An expression whose value is {@code oldInstance}.167*168* @throws NullPointerException if {@code out} is {@code null}169* and this value is used in the method170*/171protected abstract Expression instantiate(Object oldInstance, Encoder out);172173/**174* Produce a series of statements with side effects on {@code newInstance}175* so that the new instance becomes <em>equivalent</em> to {@code oldInstance}.176* In the specification of this method, we mean by equivalent that, after the method177* returns, the modified instance is indistinguishable from178* {@code newInstance} in the behavior of all methods in its179* public API.180* <p>181* The implementation typically achieves this goal by producing a series of182* "what happened" statements involving the {@code oldInstance}183* and its publicly available state. These statements are sent184* to the output stream using its {@code writeExpression}185* method which returns an expression involving elements in186* a cloned environment simulating the state of an input stream during187* reading. Each statement returned will have had all instances188* the old environment replaced with objects which exist in the new189* one. In particular, references to the target of these statements,190* which start out as references to {@code oldInstance} are returned191* as references to the {@code newInstance} instead.192* Executing these statements effects an incremental193* alignment of the state of the two objects as a series of194* modifications to the objects in the new environment.195* By the time the initialize method returns it should be impossible196* to tell the two instances apart by using their public APIs.197* Most importantly, the sequence of steps that were used to make198* these objects appear equivalent will have been recorded199* by the output stream and will form the actual output when200* the stream is flushed.201* <p>202* The default implementation, calls the {@code initialize}203* method of the type's superclass.204*205* @param type the type of the instances206* @param oldInstance The instance to be copied.207* @param newInstance The instance that is to be modified.208* @param out The stream to which any initialization statements should be written.209*210* @throws NullPointerException if {@code out} is {@code null}211*/212protected void initialize(Class<?> type,213Object oldInstance, Object newInstance,214Encoder out)215{216Class<?> superType = type.getSuperclass();217PersistenceDelegate info = out.getPersistenceDelegate(superType);218info.initialize(superType, oldInstance, newInstance, out);219}220}221222223