/*
 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.java2d;

This interface is implemented by classes which contain complex state so that other objects can track whether or not their state has changed since earlier interactions with the object.

The suggested usage pattern for code that manages some trackable data is as follows:

class Trackable implements StateTrackable {
    TrackedInfo data;
    State curState = STABLE;
    StateTracker curTracker = null;
    // Hypothetical method to return a static piece of our tracked data.
    // Assume that Datum is either a copy of some piece of the tracked
    // data or that it is itself immutable.
    public Datum getSomeDatum(int key) {
        // No need to modify the state for this type of "get" call.
        return data.getDatum(key);
    }
    // Hypothetical method to return a raw reference to our tracked data.
    public TrackedInfo getRawHandleToInfo() {
        // Since we are returning a raw reference to our tracked
        // data and since we can not track what the caller will
        // do with that reference, we can no longer track the
        // state of this data.
        synchronized (this) {
            // Note: modifying both curState and curTracker requires
            // synchronization against the getStateTracker method.
            curState = UNTRACKABLE;
            curTracker = null;
        }
        return data;
    }
    // Hypothetical method to set a single piece of data to some
    // new static value.
    public void setSomeDatum(int key, Datum datum) {
        data.setDatum(key, datum);
        // We do not need to change state for this, we simply
        // invalidate the outstanding StateTracker objects.
        // Note: setting curTracker to null requires no synchronization.
        curTracker = null;
    }
    // getStateTracker must be synchronized against any code that
    // changes the State.
    public synchronized StateTracker getStateTracker() {
        StateTracker st = curTracker;
        if (st == null) {
            switch (curState) {
                case IMMUTABLE:   st = StateTracker.ALWAYS_CURRENT; break;
                case STABLE:      st = new Tracker(this); break;
                case DYNAMIC:     st = StateTracker.NEVER_CURRENT; break;
                case UNTRACKABLE: st = StateTracker.NEVER_CURRENT; break;
            }
            curTracker = st;
        }
        return st;
    }
    static class Tracker implements StateTracker {
        Trackable theTrackable;
        public Tracker(Trackable t) {
            theTrackable = t;
        }
        public boolean isCurrent() {
            return (theTrackable.curTracker == this);
        }
    }
}
Note that the mechanism shown above for invalidating outstanding StateTracker objects is not the most theoretically conservative way to implement state tracking in a "set" method. There is a small window of opportunity after the data has changed before the outstanding StateTracker objects are invalidated and where they will indicate that the data is still the same as when they were instantiated. While this is technically inaccurate, it is acceptable since the more conservative approaches to state management are much more complex and cost much more in terms of performance for a very small gain in correctness. For example:

The most conservative approach would be to synchronize all accesses and all modifications to the data, including its State. This would require synchronized blocks around some potentially large bodies of code which would impact the multi-threaded scalability of the implementation. Further, if data is to be coordinated or transferred between two trackable objects then both would need to be synchronized raising the possibility of deadlock unless some strict rules of priority for the locking of the objects were established and followed religiously. Either or both of these drawbacks makes such an implementation infeasible.

A less conservative approach would be to change the state of the trackable object to DYNAMIC during all modifications of the data and then to change it back to STABLE after those modifications are complete. While this state transition more accurately reflects the temporary loss of tracking during the modification phase, in reality the time period of the modifications would be small in most cases and the 2 changes of state would each require synchronization.

In comparison the act of setting the curTracker reference to null in the usage pattern above effectively invalidates all outstanding Tracker objects as soon as possible after the change to the data and requires very little code and no synchronization to implement.

In the end it is up to the implementor of a StateTrackable object how fine the granularity of State updates should be managed based on the frequency and atomicity of the modifications and the consequences of returning an inaccurate State for a particularly small window of opportunity. Most implementations are likely to follow the liberal, but efficient guidelines found in the usage pattern proposed above.

Since:1.7
/** * This interface is implemented by classes which contain complex state * so that other objects can track whether or not their state has changed * since earlier interactions with the object. * <p> * The suggested usage pattern for code that manages some trackable data * is as follows: * <pre> * class Trackable implements StateTrackable { * TrackedInfo data; * State curState = STABLE; * StateTracker curTracker = null; * // Hypothetical method to return a static piece of our tracked data. * // Assume that Datum is either a copy of some piece of the tracked * // data or that it is itself immutable. * public Datum getSomeDatum(int key) { * // No need to modify the state for this type of "get" call. * return data.getDatum(key); * } * // Hypothetical method to return a raw reference to our tracked data. * public TrackedInfo getRawHandleToInfo() { * // Since we are returning a raw reference to our tracked * // data and since we can not track what the caller will * // do with that reference, we can no longer track the * // state of this data. * synchronized (this) { * // Note: modifying both curState and curTracker requires * // synchronization against the getStateTracker method. * curState = UNTRACKABLE; * curTracker = null; * } * return data; * } * // Hypothetical method to set a single piece of data to some * // new static value. * public void setSomeDatum(int key, Datum datum) { * data.setDatum(key, datum); * // We do not need to change state for this, we simply * // invalidate the outstanding StateTracker objects. * // Note: setting curTracker to null requires no synchronization. * curTracker = null; * } * // getStateTracker must be synchronized against any code that * // changes the State. * public synchronized StateTracker getStateTracker() { * StateTracker st = curTracker; * if (st == null) { * switch (curState) { * case IMMUTABLE: st = StateTracker.ALWAYS_CURRENT; break; * case STABLE: st = new Tracker(this); break; * case DYNAMIC: st = StateTracker.NEVER_CURRENT; break; * case UNTRACKABLE: st = StateTracker.NEVER_CURRENT; break; * } * curTracker = st; * } * return st; * } * * static class Tracker implements StateTracker { * Trackable theTrackable; * public Tracker(Trackable t) { * theTrackable = t; * } * public boolean isCurrent() { * return (theTrackable.curTracker == this); * } * } * } * </pre> * Note that the mechanism shown above for invalidating outstanding * StateTracker objects is not the most theoretically conservative * way to implement state tracking in a "set" method. * There is a small window of opportunity after the data has changed * before the outstanding StateTracker objects are invalidated and * where they will indicate that the data is still the same as when * they were instantiated. * While this is technically inaccurate, it is acceptable since the more * conservative approaches to state management are much more complex and * cost much more in terms of performance for a very small gain in * correctness. * For example: * <p> * The most conservative approach would be to synchronize all accesses * and all modifications to the data, including its State. * This would require synchronized blocks around some potentially large * bodies of code which would impact the multi-threaded scalability of * the implementation. * Further, if data is to be coordinated or transferred between two * trackable objects then both would need to be synchronized raising * the possibility of deadlock unless some strict rules of priority * for the locking of the objects were established and followed * religiously. * Either or both of these drawbacks makes such an implementation * infeasible. * <p> * A less conservative approach would be to change the state of the * trackable object to DYNAMIC during all modifications of the data * and then to change it back to STABLE after those modifications * are complete. * While this state transition more accurately reflects the temporary * loss of tracking during the modification phase, in reality the * time period of the modifications would be small in most cases * and the 2 changes of state would each require synchronization. * <p> * In comparison the act of setting the <code>curTracker</code> * reference to null in the usage pattern above effectively invalidates * all outstanding <code>Tracker</code> objects as soon as possible * after the change to the data and requires very little code and no * synchronization to implement. * <p> * In the end it is up to the implementor of a StateTrackable object * how fine the granularity of State updates should be managed based * on the frequency and atomicity of the modifications and the * consequences of returning an inaccurate State for a particularly * small window of opportunity. * Most implementations are likely to follow the liberal, but efficient * guidelines found in the usage pattern proposed above. * * @since 1.7 */
public interface StateTrackable {
An enumeration describing the current state of a trackable object. These values describe how often the complex data contained in a trackable object can be changed and whether or not it makes sense to try to track the data in its current state.
See Also:
  • getState.getState
Since:1.7
/** * An enumeration describing the current state of a trackable * object. * These values describe how often the complex data contained * in a trackable object can be changed and whether or not it * makes sense to try to track the data in its current state. * @see StateTrackable#getState * @since 1.7 */
public enum State {
The complex data will never change again. Information related to the current contents of the complex data can be calculated and cached indefinitely with no further checks to see if the information is stale.
/** * The complex data will never change again. * Information related to the current contents of the complex * data can be calculated and cached indefinitely with no * further checks to see if the information is stale. */
IMMUTABLE,
The complex data is currently stable, but could change at some point in the future. Information related to the current contents of the complex data can be calculated and cached, but a StateTracker should be used to verify the freshness of such precalculated data before each future use.
/** * The complex data is currently stable, but could change at * some point in the future. * Information related to the current contents of the complex * data can be calculated and cached, but a StateTracker should * be used to verify the freshness of such precalculated data * before each future use. */
STABLE,
The complex data is currently in flux and is frequently changing. While information related to the current contents of the complex data could be calculated and cached, there is a reasonably high probability that the cached information would be found to be out of date by the next time it is used. It may also be the case that the current contents are temporarily untrackable, but that they may become trackable again in the future.
/** * The complex data is currently in flux and is frequently * changing. * While information related to the current contents of the * complex data could be calculated and cached, there is a * reasonably high probability that the cached information * would be found to be out of date by the next time it is * used. * It may also be the case that the current contents are * temporarily untrackable, but that they may become trackable * again in the future. */
DYNAMIC,
The complex data can currently be changed by external references and agents in a way that cannot be tracked. If any information about the current contents of the complex data were to be cached, there would be no way to determine whether or not that cached information was out of date.
/** * The complex data can currently be changed by external * references and agents in a way that cannot be tracked. * If any information about the current contents of the complex * data were to be cached, there would be no way to determine * whether or not that cached information was out of date. */
UNTRACKABLE, };
Returns the general state of the complex data held by this object. This return value can be used to determine if it makes strategic sense to try and cache information about the current contents of this object. The StateTracker returned from the getStateTracker() method will further aid in determining when the data has been changed so that the caches can be verified upon future uses.
See Also:
Returns:the current state of trackability of the complex data stored in this object.
Since:1.7
/** * Returns the general state of the complex data held by this * object. * This return value can be used to determine if it makes * strategic sense to try and cache information about the current * contents of this object. * The StateTracker returned from the getStateTracker() method * will further aid in determining when the data has been * changed so that the caches can be verified upon future uses. * @return the current state of trackability of the complex * data stored in this object. * @see #getStateTracker * @since 1.7 */
public State getState();
Returns an object which can track future changes to the complex data stored in this object. If an external agent caches information about the complex data of this object, it should first get a StateTracker object from this method so that it can check if such information is current upon future uses. Note that a valid StateTracker will always be returned regardless of the return value of getState(), but in some cases the StateTracker may be a trivial implementation which always returns the same value from its isCurrent method.
  • If the current state is IMMUTABLE, this StateTracker and any future StateTracker objects returned from this method will always indicate that the state has not changed.
  • If the current state is UNTRACKABLE, this StateTracker and any future StateTracker objects returned from this method will always indicate that the state has changed.
  • If the current state is DYNAMIC, this StateTracker may always indicate that the current state has changed, but another StateTracker returned from this method in the future when the state has changed to STABLE will correctly track changes.
  • Otherwise the current state is STABLE and this StateTracker will indicate whether or not the data has changed since the time at which it was fetched from the object.
See Also:
Returns:an object implementing the StateTracker interface that tracks whether changes have been made to the complex contents of this object since it was returned.
Since:1.7
/** * Returns an object which can track future changes to the * complex data stored in this object. * If an external agent caches information about the complex * data of this object, it should first get a StateTracker * object from this method so that it can check if such * information is current upon future uses. * Note that a valid StateTracker will always be returned * regardless of the return value of getState(), but in some * cases the StateTracker may be a trivial implementation * which always returns the same value from its * {@link StateTracker#isCurrent isCurrent} method. * <ul> * <li>If the current state is {@link State#IMMUTABLE IMMUTABLE}, * this StateTracker and any future StateTracker objects * returned from this method will always indicate that * the state has not changed.</li> * <li>If the current state is {@link State#UNTRACKABLE UNTRACKABLE}, * this StateTracker and any future StateTracker objects * returned from this method will always indicate that * the state has changed.</li> * <li>If the current state is {@link State#DYNAMIC DYNAMIC}, * this StateTracker may always indicate that the current * state has changed, but another StateTracker returned * from this method in the future when the state has changed * to {@link State#STABLE STABLE} will correctly track changes.</li> * <li>Otherwise the current state is {@link State#STABLE STABLE} * and this StateTracker will indicate whether or not the * data has changed since the time at which it was fetched * from the object.</li> * </ul> * @return an object implementing the StateTracker interface * that tracks whether changes have been made to the complex * contents of this object since it was returned. * @see State * @see #getState * @since 1.7 */
public StateTracker getStateTracker(); }