Package jsky.science

Source Code of jsky.science.AbstractScienceObject$EventMonitor

//=== File Prolog =============================================================
//  This code was adapted by NASA, Goddard Space Flight Center, Code 588
//  for the Scientist's Expert Assistant (SEA) project.
//
//--- Contents ----------------------------------------------------------------
//  class AbstractScienceObject
//
//--- Description -------------------------------------------------------------
//  AbstractScienceObject provides minimal implementation for the
//  ScienceObjectModel interface.
//
//--- Development History -----------------------------------------------------
//
//  05/03/00  S. Grosvenor / 588 Booz-Allen
//    Original implementation, part of breakdown of old ScienceObject class
//      Code brought in primarily from old event.ReplaceablePropertyChangeSupport
//      and old science.ScienceObject classes.
//
//    10/09/00    S. Grosvenor / 588 Booz-Allen
//      Fixed bugs/inconsistencies in clone().  Also improved tracing on
//      propertychanges and clone messages.
//
//--- DISCLAIMER---------------------------------------------------------------
//
//  This software is provided "as is" without any warranty of any kind, either
//  express, implied, or statutory, including, but not limited to, any
//  warranty that the software will conform to specification, any implied
//  warranties of merchantability, fitness for a particular purpose, and
//  freedom from infringement, and any warranty that the documentation will
//  conform to the program, or any warranty that the software will be error
//  free.
//
//  In no event shall NASA be liable for any damages, including, but not
//  limited to direct, indirect, special or consequential damages, arising out
//  of, resulting from, or in any way connected with this software, whether or
//  not based upon warranty, contract, tort or otherwise, whether or not
//  injury was sustained by persons or property or otherwise, and whether or
//  not loss was sustained from or arose out of the results of, or use of,
//  their software or services provided hereunder.
//=== End File Prolog====================================================================

package jsky.science;

import jsky.util.ReplaceablePropertyChangeListener;
import jsky.util.ReplacementEvent;
import jsky.util.ReplacementVetoException;
import jsky.util.ReplaceablePropertyVetoException;

import java.beans.PropertyChangeEvent;
import java.util.HashMap;
import java.util.Map;

import java.io.Serializable;

/**
* Basic implementation of the ScienceObjectModel interface.
* <p/>
* <P>This code was originally developed by NASA, Goddard Space Flight Center, Code 588
* for the Scientist's Expert Assistant (SEA) project for Next Generation
* Space Telescope (NGST).
*/
public abstract class AbstractScienceObject implements ScienceObjectModel,
        Cloneable,
        Serializable,
        Comparable {

    /**
     * The Stream Unique Identifier for this class.
     */
    private static final long serialVersionUID = 1L;

    /**
     * name of the object
     */
    private String fName;

    /**
     * true if tracing is on for this object
     */
    private boolean fTracing = false;

    /**
     * point to parent node, may be null
     */
    transient private ScienceObjectNodeModel fParent;

    /**
     * array of listeners to this object
     */
    transient private ReplaceablePropertyChangeListener[] fListeners;


    /**
     * event monitors are for testing purposes only (see junit.SeaTestCase)
     */
    transient private static EventMonitor[] sMonitors;

    /**
     * list of last index number for creating default names
     * for new objects.
     */
    transient private static Map<String, Integer> sClassIndices = null;

    public AbstractScienceObject() {
        this(null);
    }

    public AbstractScienceObject(String inName) {
        super();
        setParent(null);
        fName = inName;
    }

    /**
     * local implementation: returns the name of the object, unless thats null
     * in which case it returns call the superclass (Object)'s toString()
     */
    public String toString() {
        if ((fName == null) || (fName.length() == 0)) {
            return super.toString();
        } else {
            return fName;
        }
    }

    /**
     * Returns the parent object in a hierarchy of objects.  May return null.
     */
    public ScienceObjectNodeModel getParent() {
        return fParent;
    }

    /**
     * Sets the parent in the hierarchy.  Note that this does not send
     * out any change notification, to avoid endless looping
     */
    public void setParent(ScienceObjectNodeModel model) {
        fParent = model;
    }

    /**
     * default implement: always returns false.  Subclasses that wish to
     * implement a "pending" capability should override this method
     */
    public boolean isPending() {
        return false;
    }

    /**
     * contains a local exception relevant to a problem in the current state
     * of the object
     */
    private Exception fException;

    /**
     * returns the local exception (if any).  Returns null indicates that there
     * are no current problems with the state of this object
     */
    public Exception getException() {
        return fException;
    }

    /**
     * sets an exception.  This is public, but would normally be used within
     * the object when a problems arises.
     */
    public void setException(Exception e) {
        fException = e;
    }

    /**
     * default implementation: the object is valid if and only if there is
     * no specified local exception
     */
    public boolean isValid() {
        return (fException == null);
    }

    /**
     * default implementation: always returns false, should be overridden by
     * subclasses that want to implement a delayed updating capability
     */
    public boolean isHolding() {
        return false;
    }

    /**
     * default implementation: does nothing
     */
    public void setHolding(boolean hold) {
    }

    /**
     * default implementation: fires superclass (Object)'s clone and suppresses the
     * CloneNotSupportedException, returning null if super.clone() fails
     */
    public Object clone() {
        AbstractScienceObject cl = null;
        try {
            cl = (AbstractScienceObject) super.clone();
            cl.setName(getName());
        } catch (CloneNotSupportedException ignored) {
        }
        return cl;
    }

    /**
     * Default implementation: compares the Names of the objects if they
     * are ScienceObjectModels.  If compared object is not a ScienceObjectModel,
     * returns -1.
     */
    public int compareTo(Object o) {
        try {
            return getName().compareTo(((ScienceObjectModel) o).getName());
        } catch (Exception e) {
            return -1;
        }
    }

    /**
     * Default implementation: checks for same name only
     */
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof AbstractScienceObject)) {
            return false;
        }
        AbstractScienceObject that = (AbstractScienceObject) obj;

        return areNamesEqual(that);
    }

    /**
     * returns the Name property.  Returns empty string if the name is null.
     * Will not return null!
     */
    public String getName() {
        if (fName == null) {
            fName = createDefaultName();
        }
        return ((fName == null) ? "" : fName);
    }

    /**
     * checks equality of the Name property.
     */
    protected boolean areNamesEqual(ScienceObjectModel target) {
        String targName = target.getName();
        return fName != null && fName.equals(targName);
    }

    /**
     * Creates and returns a default name for an object.  May not be pretty: the default
     * name is the last part of the class name and a numerical suffix.  Should
     * be a unique name within each program run.
     */
    protected String createDefaultName() {
        if (sClassIndices == null) {
            sClassIndices = new HashMap<String, Integer>();
        }
        String cName = this.getClass().toString();
        cName = cName.substring(cName.lastIndexOf(".") + 1);
        Integer index = sClassIndices.get(cName);
        if (index == null) {
            // first time for this class
            index = 1;
        } else {
            index = index + 1;
        }
        sClassIndices.put(cName, index);
        return cName + index;
    }

    /**
     * Sets the Name property.
     */
    public void setName(String inName) {
        String oldName = fName;
        fName = inName;
        firePropertyChange(NAME_PROPERTY, oldName, inName);
    }

    /**
     * Convenience method for storing a "user friendly" name for an object.
     * Should return a "pretty" label for a multi-line pane, something nicer
     * (and non-recursive) than plain old toString().
     * However, in this default implementation, toString() is what you get.
     */
    public String getLabel() {
        return toString();
    }

    /**
     * Returns the current tracing mode, @see setTracing()
     */
    public boolean isTracing() {
        return (fTracing || sTraceAll);
    }

    private static boolean sTraceAll = false;

    /**
     * Sets a global tracing flag to turn tracing on for all AbstractScienceObjects.
     *
     * @see #setTracing
     */
    public static void setTraceAll(boolean t) {
        sTraceAll = t;
    }

    /**
     * Sets debugging tracing on or off.  This is a debugging feature for developers.
     * When set to true, trace messages will be sent to the debug destination
     * for _every_ call to firePropertyChange or fireReplaceObject.
     * <P>This is particularly useful for tracking down problems in clone() or
     * replaceObject().
     */
    public void setTracing(boolean onOff) {
        if (!sTraceAll) {
            writeDebug(getObjectIdString(), "tracing has been turned " +
                    (onOff ? "ON" : "OFF"));
            fTracing = onOff;
        }
    }

    /**
     * Convenience method for cloning.  Useful where subclassed object does not
     * want to have the listener vector "cloned" but rather emptied
     */
    public void clearAllListeners() {
        fListeners = null;
    }

    /**
     * add a listener
     *
     * @param listener The ReplaceablePropertyChangeListener to be added
     */
    public void addPropertyChangeListener(ReplaceablePropertyChangeListener listener) {
        synchronized (this) {
            ReplaceablePropertyChangeListener[] els = null;
            if (fListeners != null) {
                for (ReplaceablePropertyChangeListener fListener : fListeners) {
                    if (fListener == listener) {
                        return; // already have it, don't add it
                    }
                }
                int length = fListeners.length;
                els = new ReplaceablePropertyChangeListener[length + 1];
                System.arraycopy(fListeners, 0, els, 0, length);
            } else {
                els = new ReplaceablePropertyChangeListener[1];
            }
            els[els.length - 1] = listener;
            fListeners = els;
        }
    }

    /**
     * Remove a listener
     *
     * @param listener The ReplaceablePropertyChangeListener to be removed
     */
    public void removePropertyChangeListener(
            ReplaceablePropertyChangeListener listener) {
        if (fListeners == null) {
            return;
        }
        synchronized (this) {
            int length = fListeners.length;
            for (ReplaceablePropertyChangeListener fListener : fListeners) {
                if (fListener == listener) {
                    length--;
                }
            }
            if (length == 0) {
                fListeners = null;
                return;
            }
            ReplaceablePropertyChangeListener[] els = new ReplaceablePropertyChangeListener[length];
            int nexti = 0;
            for (ReplaceablePropertyChangeListener fListener : fListeners) {
                if (fListener != listener) {
                    els[nexti++] = fListener;
                }
            }
            fListeners = els;
        }
    }

    /**
     * add an event monitor, each monitor will be notified of of all
     * PropertyChangeEvents fired by this object.  Expected to be of use
     * for testing and debugging ONLY
     *
     * @param listener An event monitor to be added
     */
    public static void addEventMonitor(EventMonitor listener) {
        EventMonitor[] els = null;
        if (sMonitors != null) {
            for (EventMonitor sMonitor : sMonitors) {
                if (sMonitor == listener) {
                    return; // already have it, don't add it
                }
            }
            int length = sMonitors.length;
            els = new EventMonitor[length + 1];
            System.arraycopy(sMonitors, 0, els, 0, length);
        } else {
            els = new EventMonitor[1];
        }
        els[els.length - 1] = listener;
        sMonitors = els;
    }

    /**
     * Remove a listener
     *
     * @param listener The ReplaceablePropertyChangeListener to be removed
     */
    public static void removeEventMonitor(EventMonitor listener) {
        if (sMonitors == null) {
            return;
        }

        int length = sMonitors.length;
        for (EventMonitor sMonitor : sMonitors) {
            if (sMonitor == listener) {
                length--;
            }
        }
        if (length == 0) {
            sMonitors = null;
            return;
        }
        EventMonitor[] els = new EventMonitor[length];
        int nexti = 0;
        for (EventMonitor sMonitor : sMonitors) {
            if (sMonitor != listener) {
                els[nexti++] = sMonitor;
            }
        }
        sMonitors = els;
    }

    public void notifyEventMonitors(Object target, java.util.EventObject event) {
        if (sMonitors != null) {
            for (EventMonitor sMonitor : sMonitors) {
                sMonitor.eventFired(this, target, event);
            }
        }
    }

    /**
     * default implementation. Propogates propertyChanges to all listeners.
     * This is a basic component of the hierarchy of changes events supported
     * by AbstractScienceObject.
     */
    public void propertyChange(PropertyChangeEvent ev) {
        if (!ev.getPropertyName().equals(NAME_PROPERTY)) {
            firePropertyChange(getName() + "." + ev.getPropertyName(), ev.getOldValue(), ev.getNewValue());
        }
    }

    /**
     * Convenience debugging method for classes that do not support replaceObject() but
     * find it has been called anyway.
     * Displays a message indicating that the replaceObject() method has not yet been
     * "implemented".  Should be called by Objects who want to postpont defining replaceObject
     * but gives centralize message and tracking ability
     */
    public static void replaceObjectNYI(Object source, ReplacementEvent event) {
        System.err.println("[ERROR] " + source +
                ": Unimplemented replaceObject call, oldv=" +
                getObjectIdString(event.getOldValue()) +
                " newv=" + getObjectIdString(event.getNewValue())
        );
    }

    /**
     * Default implementation.  Does nothing
     */
    public void replaceObject(ReplacementEvent ev)
            throws ReplacementVetoException {
        // do nothing
    }

    /**
     * Report a bound property update to any registered listeners.
     * No event is fired if old and new are equal and non-null.
     *
     * @param propertyName The programmatic name of the property
     * that was changed.
     * @param oldValue The old value of the property.
     * @param newValue The new value of the property.
     */
    public void firePropertyChange(String propertyName,
                                   Object oldValue, Object newValue) {
        firePropertyChange(propertyName, oldValue, newValue, isTracing());
    }

    /**
     * tracks indentation in recursive sequence of debug messages
     */
    private static String debugIndent = "";

    /**
     * tracks indentation in recursive sequence of debug messages
     */
    private static String replaceIndent = "";

    /**
     * Report a bound property update to any registered listeners.
     * No event is fired if old and new are equal and non-null.
     *
     * @param propertyName The programmatic name of the property
     * that was changed.
     * @param oldValue The old value of the property.
     * @param newValue The new value of the property.
     */
    public void firePropertyChange(String propertyName,
                                   Object oldValue, Object newValue, boolean trace) {
        if (oldValue != null && oldValue.equals(newValue)) {
            return;
        }
        if (oldValue == null && newValue == null) {
            return;
        }

        ReplaceablePropertyChangeListener[] localListeners = fListeners;
        if (localListeners == null) {
            return;
        }

        PropertyChangeEvent evt = new PropertyChangeEvent(
                this,
                propertyName, oldValue, newValue);

        if (trace) {
            writeDebug(debugIndent + getObjectIdString(this) + ".firepropertyChange", propertyName);
            debugIndent = debugIndent + "  ";
        }

        for (ReplaceablePropertyChangeListener target : localListeners) {
            if (trace) {
                writeDebug(debugIndent, getObjectIdString(target));
            }

            try {
                notifyEventMonitors(target, evt);
                target.propertyChange(evt);
            } catch (ReplaceablePropertyVetoException ex) {
                writeError(this,
                        "Unexpected ReplaceablePropertyVetoException returned and ignored, " +
                                ex.toString());
            }
        }
        if (trace) {
            debugIndent = debugIndent.substring(2);
        }
    }

    /**
     * Report a bound property update to any registered listeners.
     * No event is fired if old and new are equal and non-null.
     *
     * @param propertyName The programmatic name of the property
     * that was changed.
     * @param oldValue The old value of the property.
     * @param newValue The new value of the property.
     */
    public void fireVetoableChange(String propertyName,
                                   Object oldValue, Object newValue)
            throws ReplaceablePropertyVetoException {
        fireVetoableChange(propertyName, oldValue, newValue, isTracing());
    }

    /**
     * Report a bound property update to any registered fListeners.
     * If any listener throws a ReplaceablePropertyVetoException, then this
     * method will "undo" the property change notifications and throw
     * the exception back to the original caller
     *
     * @param propertyName The programmatic name of the property
     * that was changed.
     * @param oldValue The old value of the property.
     * @param newValue The new value of the property.
     * @throws ReplaceablePropertyVetoException
     *          passed on if received from a listener
     */
    public void fireVetoableChange(String propertyName,
                                   Object oldValue, Object newValue, boolean trace)
            throws ReplaceablePropertyVetoException {
        Object eSource = this;

        if (oldValue != null && oldValue.equals(newValue)) {
            return;
        }
        if ((oldValue == null) && (newValue == null)) {
            return;
        }

        ReplaceablePropertyChangeListener[] localListeners = fListeners;
        if (localListeners == null) {
            return;
        }

        PropertyChangeEvent evt = new PropertyChangeEvent(eSource,
                propertyName, oldValue, newValue);

        for (int i = 0; i < localListeners.length; i++) {
            ReplaceablePropertyChangeListener target = localListeners[i];
            if (trace) {
                writeDebug("    calling",
                        getObjectIdString(target) + ".propertyChange");
            }
            try {
                target.propertyChange(evt);
            } catch (ReplaceablePropertyVetoException ex) {
                PropertyChangeEvent reverseEvent = new PropertyChangeEvent(eSource,
                        propertyName, newValue, oldValue);
                for (int i2 = i - 1; i2 >= 0; i2--) {
                    ReplaceablePropertyChangeListener reverseTarget = localListeners[i2];
                    if (trace) {
                        writeDebug("    reversing",
                                getObjectIdString(reverseTarget) + ".propertyChange");
                    }
                    try {
                        reverseTarget.propertyChange(reverseEvent);
                    } catch (ReplaceablePropertyVetoException ex2) {
                        writeError(this,
                                "Unexpected ReplaceablePropertyVetoException returned and ignored, " +
                                        ex2.toString());
                    }
                }
                throw ex;
            }
        }
    }

    /**
     * Report a replaceObject event to any registered ReplaceablePropertyChangeListeners
     * that are also ReplaceablePropertyChangeListeners
     * No events are sent if the old and new objects are '=='. It does not
     * matter if they are .equals() true
     *
     * @param oldObject The object to be replaced by a new object
     * @param newObject The new object to replace the current object
     */
    public void fireReplaceObject(Object oldObject, Object newObject)
            throws ReplacementVetoException {
        fireReplaceObject(oldObject, newObject, isTracing());
    }

    /**
     * Report a replaceObject event to any registered propertyChangelisteners
     * that are also ReplaceablePropertyChangeListeners
     * No events are sent if the old and new objects are '=='. It does not
     * matter if they are .equals() true
     *
     * @param newObject The new object to replace the current object
     */
    public void fireReplaceObject(ReplaceablePropertyChangeListener newObject)
            throws ReplacementVetoException {
        fireReplaceObject(this, newObject, isTracing());
    }

    /**
     * Report a replaceObject event to any registered ReplaceablePropertyChangeListeners
     * that are also ReplaceablePropertyChangeListeners
     * No events are sent if the old and new objects are '=='. It does not
     * matter if they are .equals() true
     *
     * @param newObject The new object to replace the current object
     */
    public void fireReplaceObject(Object oldObject, Object newObject, boolean trace)
            throws ReplacementVetoException {
        if (oldObject == newObject) {
            return;
        }

        ReplaceablePropertyChangeListener[] localListeners = fListeners;
        if (localListeners == null) {
            return;
        }
        ReplaceablePropertyChangeListener target = null;

        int i = 0; // so is accessible in catch phrase
        if (trace) {
            writeDebug(replaceIndent + getObjectIdString(this) + ".fireReplaceObject",
                    "old=" + getObjectIdString(oldObject) + ",new=" + getObjectIdString(newObject));
            replaceIndent = replaceIndent + "  ";
        }

        try {
            ReplacementEvent evt = new ReplacementEvent(oldObject, newObject);

            for (i = 0; i < localListeners.length; i++) {
                target = localListeners[i];
                if (trace) {
                    writeDebug(replaceIndent, getObjectIdString(target));
                }
                notifyEventMonitors(target, evt);
                target.replaceObject(evt);
            }
        } catch (ReplacementVetoException ex) {
            writeDebug(this,
                    "ReplacementVetoException trapped in replaceObject, from " +
                            target.toString());

            try {
                ReplacementEvent reverseEvt = new ReplacementEvent(newObject, oldObject);
                for (int j = i - 1; j >= 0; j--) {
                    target = localListeners[j];
                    if (target != null) {
                        notifyEventMonitors(target, reverseEvt);
                        target.replaceObject(reverseEvt);
                    }
                }
            } catch (ReplacementVetoException ex2) {
                // ignore it
            }
            throw ex;
        }
        if (trace) {
            replaceIndent = replaceIndent.substring(2);
        }
    }

    /**
     * Indicates whether or not debugging messages should be generated
     */
    private static boolean showDebug = false;

    /**
     * default implementation.  Sends a formatted DEBUG message to System.err
     */
    protected void writeDebug(Object source, Object message) {
        if (showDebug) {
            System.err.println("[DEBUG] " + source + ": " + message);
        }
    }

    /**
     * default implementation.  Sends a formatted ERROR message to System.err
     */
    protected void writeError(Object source, Object message) {
        System.err.println("[ERROR] " + source + ": " + message);
    }

    /**
     * Useful for debugging output, returns a String containing the class and hashcode of
     * the specified object.
     */
    public String getObjectIdString() {
        return getObjectIdString(this);
    }

    /**
     * Useful for debugging output, returns a String containing the class and hashcode of
     * the specified object.
     */
    public static String getObjectIdString(Object obj) {
        String retval;
        if (obj == null) {
            retval = "<null>";
        } else {
            retval = obj.getClass().toString() + "@" + Integer.toHexString(obj.hashCode());
            String gov = "class ";
            int idx = gov.length();
            if (retval.indexOf(gov) >= 0) {
                retval = retval.substring(idx, retval.length());
            }
        }
        return retval;
    }


    public interface EventMonitor {

        void eventFired(Object from, Object to, java.util.EventObject event);
    }


}



TOP

Related Classes of jsky.science.AbstractScienceObject$EventMonitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.