ca.sfu.federation.model.Component.java Source code

Java tutorial

Introduction

Here is the source code for ca.sfu.federation.model.Component.java

Source

/**
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program 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 for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package ca.sfu.federation.model;

import ca.sfu.federation.ApplicationContext;
import ca.sfu.federation.model.annotations.Update;
import ca.sfu.federation.model.exception.NonExistantMethodException;
import ca.sfu.federation.model.exception.NonExistantUpdateAnnotationException;
import ca.sfu.federation.utils.INamedUtils;
import ca.sfu.federation.utils.ImageIconUtils;
import java.awt.Graphics;
import java.awt.Image;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import org.apache.commons.lang.exception.ExceptionUtils;

/**
 * <p>
 * Component is an implementation of a Processing Element of the Systolic Array.
 * Component performs user defined units of computing, such as creating geometric
 * objects, performing logical operations on objects or sets of objects, and 
 * selecting objects.  Component may produce side effects on other Components or 
 * on the Systolic Array itself.
 * </p>
 * <p>
 * The user may instantiate a Component without needing to provide any 
 * parameters except the Context in which the Component should be registered.  
 * This is called an Anonymous Processing Element.  However, to
 * employ the Processing Element for any purpose, one of ???
 * the SAE's existing "Update Methods" must be selected for execution whenever a
 * call to the primary update method is received.  Update methods define some
 * arbitrary unit of computing and may produce side effects in the environment.
 * </p>
 * <p>
 * When the user selects the update method to be employed for the SAE instance,
 * the selected update method signature is examined and a Hashtable is created
 * to hold the required input values for the method.  The user provides the
 * values required by the update method, and those values are stored in the
 * Hashtable.  The user provided values may come in the form of object name
 * references or expressions.  The SAE parses the user input, confirms whether
 * the user input produces a value of the correct type, and assigns the value
 * into the Hashtable.
 * </p>
 * <p>
 * When the SystolicArray calls the SAE's main update method ("Update"), Update
 * maps the user defined values in the Hashtable to the method parameters and
 * then invokes the update method.  SAE's may have a result object associated
 * with them.  That result object (either single or composite) is stored in the
 * object storage and a reference to that object is maintained in the SAE. When
 * the update call is complete, the SAE returns a value of 'true' to inform the
 * SystolicArray that it has updated successfully.
 * </p>
 *
 * TODO: need to throw an exception if the user attempts to set an input property but the update method has not been set
 * TODO: need to study the cycle of setting the update method/method name, serializing/deserializing the object, etc. to ensure that it is always set
 *
 * @author Davis Marques
 */
public class Component extends Observable implements IViewable, IGraphable, IUpdateable, Observer, Serializable {

    public static String DEFAULT_NAME = "Component";

    private static final Logger logger = Logger.getLogger(Component.class.getName());

    private String name; // the name for this node
    private String basename; // default name prefix

    private IContext context; // the parent context
    private String updateMethodName; // the name of the user selected update method
    private transient Method updateMethod; // the user selected update method
    private InputTable inputTable; // inputTable corresponding with the user selected update method signature
    private IViewable result; // displayable result object .. in theory the result may not be displayable

    // display properties
    private ImageIcon icon; // icon representation of component
    private Image thumbnail; // thumbnail representation of component
    private boolean visible; // true if the result object should be displayed

    //--------------------------------------------------------------------------

    /**
      * Component constructor.
      * @param Name Name
      */
    public Component(String Name) {
        this.name = Name;
        this.icon = ImageIconUtils.loadIconById("component-icon");
        this.thumbnail = ImageIconUtils.loadIconById("component-thumbnail").getImage();
    }

    //--------------------------------------------------------------------------

    /**
     * Delete the object from its context.
     */
    public void delete() {
        // tell observers to release me
        logger.log(Level.INFO, "Component signalled Observers to release and delete object");
        this.setChanged();
        this.notifyObservers(Integer.valueOf(ApplicationContext.EVENT_ELEMENT_DELETE_REQUEST));
    }

    /**
     * Draw model object.
     */
    public void draw(Graphics g) {
    }

    /**
     * Get canonical name.
     * @return Fully qualified name.
     */
    public String getCanonicalName() {
        return INamedUtils.getCanonicalName(this);
    }

    /**
     * Get the context for this Element.
     * @return The context for this Element.
     */
    public IContext getContext() {
        return this.context;
    }

    /**
     * Get the Elements on which this Element is dependant.
     * @return Elements upon which this Element is dependant.
     */
    public Map<String, INamed> getDependancies() {
        return this.inputTable.getDependancies();
    }

    /**
     * Get icon representation for this Component.
     * @return Icon
     */
    public ImageIcon getIcon() {
        return this.icon;
    }

    /**
     * Get update method input keys.
     * @return Inputs.
     */
    public List<String> getInputKeys() {
        ArrayList<String> keys = new ArrayList<String>();
        Input[] inputs = this.inputTable.getInputs();
        for (int i = 0; i < inputs.length; i++) {
            Input input = inputs[i];
            keys.add(input.getName());
        }
        return keys;
    }

    /**
     * Get the input table.
     * @return InputTable
     */
    public InputTable getInputTable() {
        return this.inputTable;
    }

    /**
     * Get the name of this object.
     * @return The name of this object.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Get thumbnail image representation for this component.
     * @return Image.
     */
    public Image getThumbnail() {
        return this.thumbnail;
    }

    /**
     * Get the update method description.
     * @return Description
     */
    public String getUpdateMethodDescription() {
        return this.inputTable.getUpdateMethodDescription();
    }

    /**
     * Get the current Update method name.
     * @return Update method name.
     */
    public String getUpdateMethodName() {
        return this.updateMethodName;
    }

    /**
     * Get the list of update methods.
     * @return Update methods
     */
    public List<Method> getUpdateMethods() {
        ArrayList<Method> updatemethods = new ArrayList<Method>();
        Method[] methods = this.getClass().getDeclaredMethods();
        // is method is annotated as an update method, add it to the list
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].isAnnotationPresent(Update.class)) {
                updatemethods.add(methods[i]);
            }
        }
        return updatemethods;
    }

    /**
     * Get the visibility state of this object.
     * @return The visibility state of this object. True if visible, false otherwise.
     */
    public boolean getVisible() {
        return this.visible;
    }

    public void registerInContext(IContext Context) throws Exception {
        INamedUtils.registerInContext(Context, this);
    }

    /**
     * Restore the update method field value after deserialization.
     */
    public void restore() {
        if (!this.updateMethodName.equals("")) {
            try {
                this.setUpdateMethod(this.updateMethodName);
            } catch (NonExistantMethodException ex) {
                String stack = ExceptionUtils.getFullStackTrace(ex);
                logger.log(Level.WARNING, "Could not set update method\n\n{0}", stack);
            } catch (NonExistantUpdateAnnotationException ex) {
                String stack = ExceptionUtils.getFullStackTrace(ex);
                logger.log(Level.WARNING, "Could not set update method\n\n{0}", stack);
            }
        }
    }

    /**
     * Set the Context.
     * @param MyContext The Context for the object.
     */
    @Override
    public void setContext(IContext MyContext) {
        this.context = MyContext;
    }

    /**
     * Set the icon.
     * @param MyIcon Icon image.
     */
    protected void setIcon(ImageIcon MyIcon) {
        this.icon = MyIcon;
        this.setChanged();
        this.notifyObservers(Integer.valueOf(ApplicationContext.EVENT_ICON_CHANGE));
    }

    /**
     * Set the user input value for a particular input property.  Listen for 
     * changes on any referenced object properties.
     * @param InputName The name of the input property.
     * @param UserInput The user value for the property.
     * TODO: need to consider here what happens if there is no input table, or the named input does not exist
     */
    public void setInput(String InputName, String UserInput) {
        if (this.inputTable != null && this.inputTable.hasInput(InputName)) {
            this.inputTable.setInput(InputName, UserInput);
            if (this.inputTable.isPrimed()) {
                if (this.context instanceof IUpdateable) {
                    IUpdateable updateable = (IUpdateable) this.context;
                    updateable.update();
                }
            } else {
                // just update this element
                this.update();
            }
        }
    }

    /**
     * Set the name for this object.
     * @param Name Name for this object.
     * TODO: need to make sure that the new name does not already exist in the context.  there should be a vetoable change listener in play here
     */
    public void setName(String Name) {
        String old = this.name;
        this.name = Name;
        // generate change event
        this.setChanged();
        this.notifyObservers(Integer.valueOf(ApplicationContext.EVENT_NAME_CHANGE));
    }

    /**
     * Set the update method for this element, create an input table to hold
     * the required parameter input fields.
     * @param UpdateMethodName The update method name.
     * @throws NonExistantMethodException The named method does not exist for this SAE.
     * @throws NonExistantUpdateAnnotationException The named method does not have an Update annotation.
     */
    public void setUpdateMethod(String UpdateMethodName)
            throws NonExistantMethodException, NonExistantUpdateAnnotationException {
        // init
        Method[] method = this.getClass().getDeclaredMethods();
        boolean found = false;
        int i = 0;
        // for each method
        while (!found && i < method.length) {
            // if it is the named method
            if (method[i].getName().equals(UpdateMethodName) && method[i].isAnnotationPresent(Update.class)) {
                // set it as the current update method
                this.updateMethod = method[i];
                this.updateMethodName = UpdateMethodName;
                found = true;
            } else {
                throw new NonExistantUpdateAnnotationException();
            }
            i++;
        }
        // if the update method was not found, throw an exception
        if (!found) {
            throw new NonExistantMethodException();
        }
        // create an input property table for the new update method
        try {
            this.inputTable.generateInputs(this.updateMethod);
        } catch (Exception ex) {
            String stack = ExceptionUtils.getFullStackTrace(ex);
            logger.log(Level.WARNING, "Could not get inputs\n\n{0}", stack);
        }
        //        // notify observers
        //        this.setChanged();
        //        this.notifyObservers(Integer.valueOf(ApplicationContext.EVENT_ELEMENT_CHANGE));
    }

    /**
     * Set the visibility state of the object.
     * @param State Visibility state of the object.  True if the object should be displayed, false otherwise.
     */
    @Override
    public void setVisible(boolean State) {
        this.visible = State;
        // notify observers
        this.setChanged();
        this.notifyObservers(Integer.valueOf(ApplicationContext.EVENT_ELEMENT_CHANGE));
    }

    /**
     * Update the state of this Element.
     * @return True if the update succeeded, false otherwise.
     */
    @Override
    public boolean update() {
        // init
        boolean theresult = false;
        // if an update method has been set
        if (this.updateMethod != null && this.inputTable != null) {
            // the update method has been set but the input table has not been primed;
            // we can not update, so return false to signal that the update has not succeeded
            if (!this.inputTable.isPrimed()) {
                return false;
            }
            // get update method arguements
            Object[] args = this.inputTable.getInputValues();
            // invoke the update method
            try {
                Object updateresult = this.updateMethod.invoke(this, args);
                theresult = true;
            } catch (Exception ex) {
                String stack = ExceptionUtils.getFullStackTrace(ex);
                logger.log(Level.WARNING, "Could not update state\n\n{0}", stack);
            }
        } else {
            // no update method set so just pass by for now
            theresult = true;
        }
        logger.log(Level.INFO, "Update on {0}:{1}", new Object[] { this.toString(), theresult });
        // return result
        return theresult;
    }

    /**
     * Update event.
     * @param o Observable object.
     * @param arg Update argument.
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof Integer) {
            Integer eventId = (Integer) arg;
            switch (eventId) {
            case ApplicationContext.EVENT_INPUT_CHANGE:
                // check to see if our view state parameters should be changed
                // signal viewers to redraw
                this.setChanged();
                this.notifyObservers(Integer.valueOf(ApplicationContext.EVENT_ELEMENT_CHANGE));
                break;
            }
        }
    }

}