Package jsky.science

Source Code of jsky.science.AbstractScienceObjectNode

//=== File Prolog===========================================================
//    This code was developed by NASA, Goddard Space Flight Center, Code 588
//    for the Scientist's Expert Assistant (SEA) project for Next Generation
//    Space Telescope (NGST).
//
//--- Notes-----------------------------------------------------------------
//
//--- 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 science.ScienceObject class.
//
//    10/09/00    S. Grosvenor / 588 Booz-Allen
//      Fixed bugs/inconsistencies in clone().  Also improved tracing on
//      propertychanges and clone messages.
//
//  10/10/00 S Grosvenor / 588 Booz-Allen
//      added back in a .equals() check on Node's children.
//
//--- 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 java.lang.*;
import java.util.*;
import java.io.*;

import jsky.util.ReplacementEvent;
import jsky.util.ReplacementVetoException;

/**
* Basic implementation of the ScienceObjectNodeModel.
*
* <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 AbstractScienceObjectNode extends AbstractScienceObject
        implements ScienceObjectNodeModel {

    /**
     * Whether or not more data is available from the data source.
     **/
    private boolean fMoreDataAvailable;

    /**
     * The source of the contents of the ScienceObjectModel.
     **/
    private ScienceObjectNodeModel fDataSource;

    /**
     * List of ScienceObjectModel children contained within this object.
     **/
    private List<ScienceObjectModel> fChildren;

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

    /**
     * True when delayed updating is in effect
     **/
    private boolean fHolding = false;

    /**
     * True when delayed updating is in effect AND changes are "stacked up"
     **/
    private boolean fUpdateNeeded = false;

    /**
     * True while an object is in the update process
     **/
    private boolean fPending = false;

    /**
     * Creates new AbstractScienceObjectNode with blank name and holding turned off.
     */
    public AbstractScienceObjectNode() {
        this(null, false);
    }

    /**
     * creates a new AbstractScienceObjectNode with specified name and specified initial
     * holding status.
     * <P>
     * Subclasses will often initialize with holding=true in order to
     * finish populating the object before trying to update it.
     */
    public AbstractScienceObjectNode(String inName, boolean holding) {
        super(inName);

        fHolding = holding;
        fUpdateNeeded = holding;
        fChildren = new ArrayList<ScienceObjectModel>(5);
        fMoreDataAvailable = false;
        fDataSource = null;

        initializeTransients();
    }

    /**
     * Creates new AbstractScienceObjectNode with blank name and specified holding state.
     **/
    public AbstractScienceObjectNode(boolean holding) {
        this(null, holding);
    }

    /**
     * Creates new AbstractScienceObjectNode with a name and holding turned off.
     */
    public AbstractScienceObjectNode(String inName) {
        this(inName, false);
    }

    protected static String cloneIndent = "";

    /**
     * Clones this object, and does a DEEP clone on the fChildren.
     * <P>
     * NOTE: for subclasses! If a subclass has a separate variable or list that
     * points at fChildren, remember that after this clone() method is completed
     * all fChildren will have also been cloned.
     * <P>  So subclasses may need to "repoint" variables or elemenets of lists that
     * reference child objects, but should NOT re-clone those children.
     * <p>See
     * ExposureGroup.clone() and Exposure.clone() as examples of classes that
     * need to re-proint existing pointers to correctly point at the correct elements.
     */
    public Object clone() {
        AbstractScienceObjectNode newSO = (AbstractScienceObjectNode) super.clone();

        // clone this's children into NewSO's child
        newSO.clearAllListeners();

        if (isTracing()) {
            writeDebug(cloneIndent +
                       getObjectIdString(this) + ".clone",
                       " to " + getObjectIdString(newSO)
            );
            cloneIndent = cloneIndent + "  ";
        }

        newSO.fChildren = new ArrayList<ScienceObjectModel>(5);

        // now clone all the kids and repoint newSO's fchildren to the new clones
        for (Iterator iter = this.fChildren.iterator(); iter.hasNext();) {
            ScienceObjectModel thisChild = (ScienceObjectModel) iter.next();
            ScienceObjectModel thatChild = (ScienceObjectModel) thisChild.clone();
            newSO.addChild(thatChild);
        }

        if (isTracing()) cloneIndent = cloneIndent.substring(2);
        return newSO;
    }

    /**
     * Looks up the index of a child in the children's list, matching on exact
     * equality (==) NOT on equals().
     * If match is not found, returns -1, otherwise returns
     * the index of the child in the list of children.  This differs from
     * a simple fChildren.indexOf( object) which test for .equals()
     *
     * @param child Child object to be located in the the children list
     * @return index of child or -1 of child is not in the list
     **/
    public int indexOfChild(Object child) {
        for (int childIndex = 0; childIndex < fChildren.size(); childIndex++) {
            Object next = fChildren.get(childIndex);
            if (next == child) {
                return (childIndex);
            }
        }
        return (-1);
    }

    /**
     * Copies a ScienceObjectModel and assigns it a new "Copy of" name.
     * The actual copying is deffered to the object's clone() method.
     *
     * @return  clone of the ScienceObjectModel, but with a unique name
     **/
    public ScienceObjectModel namedClone() {
        AbstractScienceObjectNode copy = (AbstractScienceObjectNode) clone();
        copy.setName(createDefaultName());

        /*
        String name = copy.getName();
        if (name.startsWith("Copy of "))
        {
            copy.setName("Copy (2) of " + name.substring(8));
        }
        else if (name.startsWith("Copy ("))
        {
            // extract copy number
            try
            {
                int copynumber = Integer.valueOf(name.substring(6, name.indexOf(")"))).intValue();
                copy.setName("Copy (" + (copynumber + 1) + ") of " + name.substring(name.indexOf(" of ") + 4));
            }
            catch (NumberFormatException ex)
            {
                copy.setName("Copy (?) of " + name.substring(name.indexOf(" of ") + 4));
            }
        }
        else
        {
            copy.setName("Copy of " + name);
        }
        */

        return copy;
    }

    /**
     * Adds equals() checks on the object's DataSources and Children
     **/
    public boolean equals(Object obj) {
        if (!super.equals(obj)) return false;
        if (!(obj instanceof AbstractScienceObjectNode)) return false;
        AbstractScienceObjectNode that = (AbstractScienceObjectNode) obj;

        if ((fDataSource == null) ? (that.fDataSource != null) : !(fDataSource.equals(that.fDataSource))) return false;
        if ((fChildren == null) ? (that.fChildren != null) : !(fChildren.equals(that.fChildren))) return false;

        return true;
    }

    /**
     * Process a request from one of a ScienceObjectModel's children to "replace"
     * itself with another object.
     **/
    public void replaceObject(ReplacementEvent ev)
            throws ReplacementVetoException {
        ScienceObjectModel oldObject = (ScienceObjectModel) ev.getOldValue();
        ScienceObjectModel newObject = (ScienceObjectModel) ev.getNewValue();
        if (oldObject == newObject) return;

        if (oldObject == this) {
            // don't update myself while i'm in limbo
            setHolding(true);

            // request to swap myself out... do this by tell firereplaceobjects on
            // my kids with the newguys' kids
            // NOTE NOTE am assuming that tree of kids is same order

            try {
                // NOTE also to get here, both old and new objects should be SO-NODES!
                Iterator oldKids = getChildren().iterator();
                Iterator newKids = ((ScienceObjectNodeModel) newObject).getChildren().iterator();
                while (oldKids.hasNext() && newKids.hasNext()) {
                    ScienceObjectModel oldC = (ScienceObjectModel) oldKids.next();
                    // I dont want to hear from this kid anymore
                    oldC.removePropertyChangeListener(this);
                    oldC.replaceObject(new ReplacementEvent(oldC,
                                                            (ScienceObjectModel) newKids.next()));
                }
            } catch (ClassCastException ee) {
            } // ok wasnt a node, cant have kids, no problem

            // now tell my listeners that I'm being replaced
            try {
                fireReplaceObject(this, newObject);
            } catch (ReplacementVetoException e) {
                writeError(this,
                           "Unexpected exception: " + e.toString());
            }
        } else {
            // see if request comes from one of my kids
            // if so, swap out the listeners and I'm done
            for (int index = 0; index < fChildren.size(); index++) {
                if (fChildren.get(index) == oldObject && newObject != oldObject) {
                    fChildren.set(index, newObject);
                    oldObject.removePropertyChangeListener(this);
                    newObject.addPropertyChangeListener(this);
                    newObject.setParent(this);
                }
            }
        }
    }

    /**
     * Prints a reasonably user-friendly description of the object to the specified
     * PrintWriter.  This is performed recursiviely through each of the child
     * objects.
     * @param pw The PrintWriter to which to send the output
     * @param indent A preliminary string (likely all blanks) to pre-pend to
     * each line of output.  Allows recursive calls to an object's children
     * to indent their output.
     */
    public void saveAsText(PrintWriter pw, int indent) {
        // in case someone forgot to implement this themselves
        String id = repeat(" ", indent);
        pw.println(id + toString());
    }

    /**
     * Support method for saveAsText.
     * Concatenates the specified input string <pre>n</pre> times.
     */
    public static String repeat(String inS, int n) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < n; i++) {
            sb.append(inS);
        }
        return sb.toString();
    }

    /**
     * Single argument varient.  Use initial indentation of 0
     */
    public void saveAsText(PrintWriter pw) {
        saveAsText(pw, 0);
    }

    /**
     * Returns true if more data is available for this science object.
     **/
    public boolean isMoreDataAvailable() {
        return fMoreDataAvailable;
    }

    /**
     * Sets whether or not more data is available for this science object from its
     * DataSource.
     **/
    public void setMoreDataAvailable(boolean more) {
        boolean old = fMoreDataAvailable;
        fMoreDataAvailable = more;

        firePropertyChange(MORE_DATA_PROPERTY, new Boolean(true), new Boolean(false));
    }

    /**
     * Attempts to retrieve more information for the specified ScienceObjectModel and
     * populate the ScienceObjectModel with that extra data.  This only makes sense
     * for ScienceObjects where isMoreDataAvailable() is true.  This method does
     * nothing in AbstractScienceObjectModelNode, but can be defined in subclasses.
     *
     * @param  forObject  retrieve more data for this science object
     **/
    public void retrieveMoreData(ScienceObjectNodeModel forObject) {
        // DataSource class does nothing, so just clear the moredata field since
        // no more data is available.  Subclasses would define this method to
        // retrieve more data.
        forObject.setMoreDataAvailable(false);
    }

    /**
     * Requests that the science object retrieve any extra data that it has
     * available from its data source.
     **/
    public void requestMoreData() {
        if (!fMoreDataAvailable) {
            return;
        }

        if (fDataSource != null) {
            fDataSource.retrieveMoreData(this);
        }
    }

    /**
     * Returns an object that describes the source of the ScienceObjectModel's data.
     **/
    public ScienceObjectNodeModel getDataSource() {
        return fDataSource;
    }

    /**
     * Sets the source of the ScienceObjectModel's data.
     **/
    public void setDataSource(ScienceObjectNodeModel s) {
        ScienceObjectNodeModel old = fDataSource;
        fDataSource = s;

        firePropertyChange(DATA_SOURCE_PROPERTY, old, fDataSource);
    }

    /**
     * Returns true if updates to the object would be "held".
     *
     * @see #processUpdates
     */
    public boolean isHolding() {
        return fHolding;
    }

    /**
     * Sets the "holding" property.
     *
     * @param inH
     * @see #processUpdates
     */
    public void setHolding(boolean inH) {
        fHolding = inH;

        if (!inH && fUpdateNeeded) {
            update();
        }
    }

    /**
     * Sets the flag that indicates whether or not updates to the object are needed.
     *
     * @param inH
     * @see #processUpdates
     */
    public void setUpdatesPending(boolean inH) {
        fUpdateNeeded = inH;
    }

    /**
     * Fires off request to update source and background counts.  If the object's
     * hold status is true, this will set a flag indicating that updates are
     * needed.  If the hold status is false, then the update process begins
     *
     * Note: subclasses should NOT override update().  Instead they should implement/override
     * processUpdates();
     *
     * @see #processUpdates
     */
    protected synchronized void update() {
        if (fHolding) {
            fUpdateNeeded = true;
        } else {
            setPending(true);
            processUpdates();
            fUpdateNeeded = false;
        }
    }

    /**
     * Called when updates to a ScienceObject is processed. Works with
     * isHolding(), setHolding(), setUpdatesPending(), and update() to provide
     * to delay a possibly lengthy update process for an object.
     * <p>In the default implementation, this method is empty, and should be overridden
     * by classes that want to be able to hold updates.
     * <P>
     * To use this feature in a subclass of ScienceObject:
     * - Use setHolding() to turn delaying of updates on/off.<br>
     * - Override processUpdates() to contain the actual code that performs
     * the updating of an object. Remember that processUpdates() or the subclass is
     * responsible for setting Pending back to false.
     * - Call update() to request that updates be made to an object. Subclasses
     * should NOT override this method, it will call processUpdates() if holding is
     * off.  If holding is on, update() will set a flag so that processUpdates() will
     * be called as soon as a setHolding(false) is received.
     * - use setUpdatesPending() to manually override the setting of the pending flag.  This
     * is rarely used in subclasses as update() and setHolding() normally manage the pending flag.
     *
     * <P>
     * Subclasses should not call processUpdates() directly.
     */
    protected void processUpdates() {
        // should be overridden to do something if subclass
        // has updates that might be held

        // if subclasses remove this setPending(false), then the subclass takes over
        // responsiblity for setting pending off when appropriate
        setPending(false);
    }

    /**
     * Returns true if the object is up-to-date, false otherwise.
     * At this level it returns true when isHolding() is false, or when
     * isHolding() is true, but there are no updates pending.
     */
    public boolean isUpToDate() {
        if (!isHolding()) {
            return true;
        } else {
            return fUpdateNeeded;
        }
    }

    /**
     * Returns true if the object is "valid".  At this level, always returns true,
     * although subclasses may override this
     **/
    public boolean isValid() {
        return true;
    }

    /**
     * Returns true when the object is in the process of performing an update.
     **/
    public boolean isPending() {
        return fPending;
    }

    /**
     * Sets the pending state.  Should rarely be needed outside of the update() method.
     **/
    protected void setPending(boolean b) {
        if (fPending == b) return;
        Boolean hold = new Boolean(fPending);
        fPending = b;
        firePropertyChange(ScienceObjectModel.PENDING_PROPERTY, hold, new Boolean(fPending));
    }

    /**
     * Turns on/off the tracing of propertychange handling in an object;
     */
    public void setTracing(boolean onOff) {
        super.setTracing(onOff);
        writeDebug(
                getObjectIdString(this),
                "tracing has been turned " +
                (onOff ? "ON" : "OFF"));
    }

    /**
     * Sets default values for all transient fields.
     **/
    protected void initializeTransients() {
        setParent(null);
    }

    /**
     * Reconstructs the object properly during deserialization.
     **/
    private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException {
        // Read all the non-transient fields
        stream.defaultReadObject();

        // Set default values for transient fields
        initializeTransients();

        // Re-add self as listener and parent to all the children
        Iterator iter = fChildren.iterator();
        while (iter.hasNext()) {
            ScienceObjectModel obj = (ScienceObjectModel) iter.next();
            obj.addPropertyChangeListener(this);
            obj.setParent(this);
        }
    }

    /**
     * Adds a ScienceObject as a "child" of the current object.
     * Automatically will handle propertyChange listending, cloning
     * and loading/writing of all official children.
     *
     * @param newKid    ScienceObjectModel to be added
     *
     **/
    public void addChild(ScienceObjectModel newKid) {
        newKid.addPropertyChangeListener(this);

        newKid.setParent(this);
        for (int i = 0; i < fChildren.size(); i++) {
            if (fChildren.get(i) == newKid) return;
        }
        // if we get here then it does not exist
        fChildren.add(newKid);
    }

    /**
     * Removes all occurrences of a ScienceObjectModel as a "child" of the current object.
     * Automatically will handle removing listeners
     *
     * @param oldKid    ScienceObjectModel to be added
     * @return the ScienceObjectModel just removed, or null if oldKid not a valid child
     *
     **/
    public ScienceObjectModel removeChild(ScienceObjectModel oldKid) {
        if (oldKid != null) {
            for (int index = 0; index < fChildren.size(); index++) {
                if (fChildren.get(index) == oldKid) {
                    fChildren.remove(index++);
                    oldKid.removePropertyChangeListener(this);
                    oldKid.setParent(null);
                    return oldKid;
                }
            }
        }
        return null;
    }

    /**
     * Removes all children from the ScienceObjectModel.
     **/
    public void removeAllChildren() {
        synchronized (fChildren) {
            // Create copy of children vector because removeChild will modify original
            Object[] copy = fChildren.toArray();

            // Call removeChild() on each child, which will remove listeners as well
            for (int i = 0; i < copy.length; i++) {
                removeChild((ScienceObjectModel) copy[i]);
            }
        }
    }

    /**
     * Replaces all occurrence of the "old" child with the new child
     * in the propertychange listings.
     *
     * @param oldKid    ScienceObjectModel to be replaced
     * @param newKid    new ScienceObjectModel to be "added"
     * @return the ScienceObjectModel just removed, or null if oldKid not a valid child
     *
     **/
    public ScienceObjectModel replaceChild(ScienceObjectModel oldKid, ScienceObjectModel newKid) {
        if (oldKid != null) {
            for (int index = 0; index < fChildren.size(); index++) {
                if (fChildren.get(index) == oldKid) {
                    fChildren.set(index, newKid);
                    oldKid.removePropertyChangeListener(this);
                    oldKid.setParent(null);

                    if (newKid != null) {
                        newKid.addPropertyChangeListener(this);
                        newKid.setParent(this);
                    }
                    return oldKid;
                }
            }
        }
        // if we get here, the oldKid didnt exist, so we had new kid
        if (newKid != null) addChild(newKid);

        return null;
    }

    /**
     * returns List of children of the science object
     * should be used only when necessary
     **/
    public List getChildren() {
        return fChildren;
    }


}
TOP

Related Classes of jsky.science.AbstractScienceObjectNode

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.