Package gov.nasa.arc.mct.components

Source Code of gov.nasa.arc.mct.components.AbstractComponent$ResetPropertiesTransaction

/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* The MCT platform is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
/**
* AbstractComponent.java Aug 18, 2008
*
* This code is property of the National Aeronautics and Space Administration
* and was produced for the Mission Control Technologies (MCT) Project.
*
*/
package gov.nasa.arc.mct.components;

import gov.nasa.arc.mct.db.util.IdGenerator;
import gov.nasa.arc.mct.gui.View;
import gov.nasa.arc.mct.platform.spi.PersistenceProvider;
import gov.nasa.arc.mct.platform.spi.Platform;
import gov.nasa.arc.mct.platform.spi.PlatformAccess;
import gov.nasa.arc.mct.platform.spi.WindowManager;
import gov.nasa.arc.mct.policy.PolicyContext;
import gov.nasa.arc.mct.policy.PolicyInfo;
import gov.nasa.arc.mct.registry.ExternalComponentRegistryImpl;
import gov.nasa.arc.mct.roles.events.AddChildEvent;
import gov.nasa.arc.mct.roles.events.PropertyChangeEvent;
import gov.nasa.arc.mct.roles.events.RemoveChildEvent;
import gov.nasa.arc.mct.services.component.ComponentTypeInfo;
import gov.nasa.arc.mct.services.component.PolicyManager;
import gov.nasa.arc.mct.services.component.ViewInfo;
import gov.nasa.arc.mct.services.component.ViewType;
import gov.nasa.arc.mct.services.internal.component.ComponentInitializer;
import gov.nasa.arc.mct.services.internal.component.Updatable;
import gov.nasa.arc.mct.util.WeakHashSet;
import gov.nasa.arc.mct.util.exception.MCTRuntimeException;

import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.SwingUtilities;


/**
* The superclass of every component type. A component is uniquely identified by its id. 
* Subclasses must be created by the platform before usage within the system, this is done using the
* <code>ComponentRegistry</code>. A component indicates that it has persistent state by providing an instance of
* {@link ModelStatePersistence} through the <code>getCapabilities</code> method.
*
* <em>Important</em> subclasses must have a public no argument constructor.
*
*/
public abstract class AbstractComponent implements Cloneable {

    /** A dummy component, used as a sentinel value. */
    public final static AbstractComponent NULL_COMPONENT;

    static {
        AbstractComponent tmpComp = new AbstractComponent() {
        };

        tmpComp.id = "0";
        tmpComp.getCapability(ComponentInitializer.class).initialize();
        NULL_COMPONENT = tmpComp;
    }

    /** The unique ID of the component, filled in by the framework. */
    private String id;

    private ComponentTypeInfo typeInfo;
    private String owner;
    private String originalOwner;
    private String creator;
    private Date creationDate;
    private AbstractComponent workUnitDelegate = null;
    private String displayName = null; // human readable name for the component.
    private String externalKey = null; // reference that can be used to contain external keys
    private Map<String, ExtendedProperties> viewRoleProperties;
    private ComponentInitializer initializer;
    private int version;
    private final AtomicBoolean isDirty = new AtomicBoolean(false);
    private boolean isStale = false;
    /** The existing manifestations of this component. */
    private final WeakHashSet<View> viewManifestations = new WeakHashSet<View>();
   
    private SoftReference<List<AbstractComponent>> cachedComponentReferences = new SoftReference<List<AbstractComponent>>(null);
    private List<AbstractComponent> mutatedComponentReferences;
   
    /**
     * Initialize the component by registering it with the component registry
     * and creating a new, empty set of view roles.
     */
    protected void initialize() {
        performInitialization();
        getCapability(Initializer.class).setInitialized();
    }

    private void performInitialization() {
        if (this.id == null) {
            this.id = IdGenerator.nextComponentId();
        }
    }
   
    /**
     * Verifies the class requirements are met for this component class.
     *
     * @param componentClass
     *            class to verify, must not be null
     * @throws IllegalArgumentException
     *             if the class does not meet the requirements
     */
    public static void checkBaseComponentRequirements(Class<? extends AbstractComponent> componentClass)
                    throws IllegalArgumentException {
        try {
            // ensure that a public no argument constructor exists. If this is
            // an inner non static class
            // there is an implicit argument of the enclosing class so this
            // scenario should be covered
            componentClass.getConstructor(new Class[0]);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(componentClass.getName()
                            + " must provide a public no argument constructor");
        }
    }
   
    /**
     * Returns the work unit delegate if any for this component. A work unit delegate will be persisted instead of this
     * component. This is generally only useful to views that are owning properties of other views or components.
     * @return work unit delegate or null if there is no delegate
     */
    public AbstractComponent getWorkUnitDelegate() {
        return workUnitDelegate;
    }
   
    /**
     * Get the type ID string for the type of the component. Component type IDs
     * are unique for each registered component type.
     *
     * @return the component type ID
     */
    public String getComponentTypeID() {
        return this.getClass().getName();
    }
   
    /**
     * Returns the views for the desired view type. This method will apply the <code>PolicyInfo.CategoryType.FILTER_VIEW_ROLE</code> policy
     * and the <code>PolicyInfo.CategoryType.PREFERRED_VIEW</code> policy before returning the appropriate list of views.
     * @param type of view to discover.
     * @return views that are appropriate for this component.
     */
    public Set<ViewInfo> getViewInfos(ViewType type) {
        Set<ViewInfo> possibleViewInfos =  PlatformAccess.getPlatform().getComponentRegistry().getViewInfos(getComponentTypeID(), type);
        Set<ViewInfo> filteredViewInfos = new LinkedHashSet<ViewInfo>();
       
        Platform platform = PlatformAccess.getPlatform();
        PolicyManager policyManager = platform.getPolicyManager();
        PolicyContext context = new PolicyContext();
        context.setProperty(PolicyContext.PropertyName.TARGET_COMPONENT.getName(), this);
        context.setProperty(PolicyContext.PropertyName.VIEW_TYPE.getName(), type);
        for (ViewInfo viewInfo : possibleViewInfos) {
            context.setProperty(PolicyContext.PropertyName.TARGET_VIEW_INFO.getName(), viewInfo);
            if (policyManager.execute(PolicyInfo.CategoryType.FILTER_VIEW_ROLE.getKey(), context)
                            .getStatus()) {
                filteredViewInfos.add(viewInfo);
            }
        }

        // if there is a preferredView then make sure this is added first in the
        // list
        for (ViewInfo viewRole : filteredViewInfos) {
            if (this.getClass().equals(viewRole.getPreferredComponentType())) {
                Set<ViewInfo> setWithPreferredViewFirst = new LinkedHashSet<ViewInfo>();
                setWithPreferredViewFirst.add(viewRole);
                setWithPreferredViewFirst.addAll(filteredViewInfos);
                filteredViewInfos = setWithPreferredViewFirst;
                break;
            }
        }
       
        return filteredViewInfos;
    }
   
    private synchronized void ensureViewPropertiesLoaded() {
        if (viewRoleProperties == null) {
            viewRoleProperties = PlatformAccess.getPlatform().getPersistenceProvider().getAllProperties(getComponentId());
        }
        assert viewRoleProperties != null;
    }
   
    private synchronized void addViewProperty(String viewRoleType, ExtendedProperties properties) {
        ensureViewPropertiesLoaded();
        if (!viewRoleProperties.containsKey(viewRoleType)) {
            this.viewRoleProperties.put(viewRoleType, properties);
        }       
    }

    /**
     * Return the unique ID for this component.
     *
     * @return the ID for the component
     */
    public String getId() {
        return this.id;
    }

    /**
     * Return the unique ID for this component. This is a synonym for
     * {@link #getId()}.
     *
     * @return the ID for the component
     */
    public String getComponentId() {
        return this.id;
    }

    /**
     * Sets the ID of this component. The framework calls this method
     * automatically. It should never be called by plugin code.
     *
     * @param id
     *            the new ID for the component
     */
    public void setId(String id) {
        if (id != null && initializer != null && initializer.isInitialized()) {
            throw new IllegalStateException("id must be set before component is initialized");
        }
        this.id = id;
    }

    /**
     * Tests whether the component is currently a leaf.
     *
     * <p>
     * A leaf component is defined as a component that will never have any
     * children. This is an <i>immutable</i> property of a component instance
     * and must not change during its lifetime. The default implementation
     * returns false (thus the default component will be a container), a
     * subclass that should identify itself as a leaf should override this
     * method and return true.
     *
     * @return true, if the component is currently a leaf. False otherwise.
     */
    public boolean isLeaf() {
        return false;
    }
   
    /**
     * Sets the user ID of the owner of the component.
     *
     * @param owner
     *            the user ID of the owner of the component
     */
    public synchronized void setOwner(String owner) {
        if (originalOwner == null) {
            originalOwner = this.owner;
        }
        this.owner = owner;
    }

    /**
     * Gets the user ID of the owner of the component prior to the owner being modified.
     * @return the user Id of the owner
     */
    public final synchronized String getOriginalOwner() {
        return originalOwner;
    }
   
    private synchronized void resetOriginalOwner() {
        originalOwner = null;
    }
   
    /**
     * Gets the user ID of the owner of the component.
     *
     * @return the component owner user ID
     */
    public synchronized String getOwner() {
        return this.owner;
    }
   
    /**
     * Gets the creator of this component.
     * @return the creator of this component
     */
    public synchronized String getCreator() {
        return creator;
    }
   
    /**
     * Gets the creation time of this component.
     * @return when this component was created
     */
    public synchronized Date getCreationDate() {
        return creationDate;
    }

    /**
     * Adds a new delegate (child) component. The model of the new delegate
     * component will be added as a new delegate model, and all view role
     * instances will be updated. The new child will be added after all existing
     * children. If the child is already present, this will move the child to
     * the end.
     *
     * @param childComponent
     *            the new child component
     */
    public final void addDelegateComponent(AbstractComponent childComponent) {
        addDelegateComponents(Collections.singleton(childComponent));
    }

    /**
     * This method is invoked by the persistence provider when the component is saved to the underlying persistence store. The
     * implementation delegates to the viewPersisted method.
     */
    public final void componentSaved() {
        final Set<View> guiComponents = getAllViewManifestations();
        if (guiComponents != null) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    for (View gui : guiComponents) {
                        gui.viewPersisted();
                    }
                }
            });
        }
    }
   
    /**
     * Gets the components which are referencing this component. Currently, only delegate components are considered.
     * The result of this method is transient and thus requires overhead for each execution.
     * @return collection which can be empty but never null of components which reference this component.
     */
    public Collection<AbstractComponent> getReferencingComponents() {
        PersistenceProvider persistenceService = PlatformAccess.getPlatform().getPersistenceProvider();
        return persistenceService.getReferences(this);
    }

    /**
     * Adds a delegate (child) component to this component. The model of the
     * delegate component will be added as a delegate model, if not already
     * present, and all the view roles will be updated by sending an
     * {@link AddChildEvent} to the view role listeners. If the model is already
     * present, the child model will be moved to the given position.
     *
     * @param childIndex
     *            the index within the children to add the new component, or -1
     *            to add at the end
     * @param childComponent
     *            the new delegate component
     */
    private void processAddDelegateComponent(int childIndex, AbstractComponent childComponent) {
        List<AbstractComponent> list = getComponents();
        if (childIndex < 0) {
            childIndex = list.size();
        }

        // If the child already exists, remove it, and adjust the insert index
        // if needed.
        int existingIndex = -1;
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getComponentId().equals(childComponent.getComponentId())) {
                existingIndex = i;
            }
        }
        if (existingIndex >= 0) {
            removeComponent(list.get(existingIndex));
            if (existingIndex < childIndex) {
                --childIndex;
            }
        }

        // Now add it again.
        addComponentAt(childIndex, childComponent);
    }

    /**
     * Determine by policy if this component can be deleted.
     *
     * @return true if this component can be deleted, false otherwise.
     */
    public final boolean canBeDeleted() {
        Platform platform = PlatformAccess.getPlatform();
        PolicyManager policyManager = platform.getPolicyManager();

        PolicyContext context = new PolicyContext();
        context.setProperty(PolicyContext.PropertyName.TARGET_COMPONENT.getName(), this);
        context.setProperty(PolicyContext.PropertyName.ACTION.getName(), 'w');
        String compositionKey = PolicyInfo.CategoryType.CAN_DELETE_COMPONENT_POLICY_CATEGORY
                        .getKey();
        return policyManager.execute(compositionKey, context).getStatus();
    }

    /**
     * Adds new delegate (child) components to this component. The model for
     * each component will be added as a delegate model, if not already present.
     * Then, all view roles will be updated by sending an {@link AddChildEvent}
     * for each new delegate added. The child will be added at the end of any
     * existing children. If the child was already present, this will move the
     * child to the end.
     *
     * <p>
     * This method is called when a drop of one or more components happens in
     * the directory tree. The canvas area of this component is unaffected.
     *
     * @param childComponents
     *            the collection of delegate components to add
     * @return true if <code>childComponents</code> are added successfully;
     *         otherwise false.
     */
    public final boolean addDelegateComponents(Collection<AbstractComponent> childComponents) {
        return addDelegateComponents(-1, childComponents);
    }

    /**
     * Adds new delegate (child) components to this component. The model for
     * each component will be added as a delegate model, if not already present.
     * Then, all view roles will be updated by sending an {@link AddChildEvent}
     * for each new delegate added. The child will be added at a given index
     * among the existing children. If the child was already present, this will
     * move the child to the new position.
     *
     * <p>
     * This method is called when a drop of one or more components happens in
     * the directory tree.
     *
     * @param childIndex
     *            the index with the children to add the new component, or -1 to
     *            add at the end
     * @param childComponents
     *            the collection of delegate components to add
     * @return true if <code>childComponents</code> are added successfully;
     *         otherwise false.
     */
    public final boolean addDelegateComponents(int childIndex,
                    Collection<AbstractComponent> childComponents) {

        if (isLeaf()) {
            throw new UnsupportedOperationException("components declared as leaf cannot be mutated");
        }
        Platform platform = PlatformAccess.getPlatform();
        PolicyManager policyManager = platform.getPolicyManager();
        PolicyContext context = new PolicyContext();
        context.setProperty(PolicyContext.PropertyName.TARGET_COMPONENT.getName(), this);
        context.setProperty(PolicyContext.PropertyName.ACTION.getName(), 'w');
        context
                        .setProperty(PolicyContext.PropertyName.SOURCE_COMPONENTS.getName(),
                                        childComponents);
        if (policyManager.execute(PolicyInfo.CategoryType.ACCEPT_DELEGATE_MODEL_CATEGORY.getKey(),
                        context).getStatus()) {
         
            if (!childComponents.isEmpty()) {
                for (AbstractComponent childComponent : childComponents) {
                    processAddDelegateComponent(childIndex, childComponent);
                }
            }
           
            addDelegateComponentsCallback(childComponents);
            return true;

        }
        return false;
    }
   
    /**
     * Invoked after any of the <code>addDelegateComponents</code> methods are invoked. The default
     * implementation does nothing.
     * @param childComponents that were added.
     */
    protected void addDelegateComponentsCallback(Collection<AbstractComponent> childComponents) {
       
    }
   
    /**
     * Removes a delegate (child) component from this component. The model of
     * the delegate component is removed as a delegate model. Then, all view
     * roles are updated by sending a {@link RemoveChildEvent}.
     *
     * @param childComponent
     *            the delegate component to remove
     */
    public final void removeDelegateComponent(AbstractComponent childComponent) {
        removeDelegateComponents(Collections.singleton(childComponent));
    }

    /**
     * Removes delegate (child) components from this component.
     *
     * @param childComponents
     *            the delegate components to remove
     */
    public synchronized void removeDelegateComponents(Collection<AbstractComponent> childComponents) {
        if (isLeaf()) {
            throw new UnsupportedOperationException("components declared as leaf cannot be mutated");
        }
        for (AbstractComponent comp : childComponents) {
            removeComponent(comp);
        }

    }

    /**
     * Get the human readable name for this component that is suitable for
     * displaying to the user in a GUI.
     *
     * If the display name has not been set, the component ID will be returned.
     *
     * Note: this method should <b>not</b> be relied on to retrieve component
     * IDs (or other component type specific identifiers such as PUI IDs).
     *
     * @return the display name of the component.
     */
    public synchronized String getDisplayName() {
        if (displayName == null) {
            return getId();
        } else {
            return displayName;
        }
    }
   
    /**
     * Get the human readable name for this component that is suitable for user display. This method differs from
     * {@link #getDisplayName()} as this method will be invoked when many components are being presented together and
     * having additional information may help the user distinguish components whose display name varies by only a small amount.
     * For example, this method may be used in presenting search results that may have the same display name so this method will be
     * invoked to help differentiate the results in the interface. The default implementation of this method will invoke {@link #getDisplayName()}.
     * @return a string representing the extended display name
     */
    public String getExtendedDisplayName() {
        return getDisplayName();
    }

    /**
     * Gets the external key for this component if it exists.
     * @return key used outside MCT if it exists, null otherwise
     */
    public synchronized String getExternalKey() {
        return externalKey;
    }
   
    /**
     * Sets the external key that allow MCT components to generically reference external entities. How this is
     * used outside MCT is considered behavior of the component and will be described further by the component author.
     * @param key used outside MCT
     */
    public synchronized void setExternalKey(String key) {
        externalKey = key;
    }
   
    /**
     * Sets the human readable display name for this component.
     *
     * @param name
     *            the new display name
     */
    public synchronized void setDisplayName(String name) {
        this.displayName = name;
    }

    /**
     * Sets the display name of the component and updates all view
     * manifestations to match, by sending a {@link PropertyChangeEvent} to each
     * view role listener.
     *
     * @param name
     *            the new display name
     */
    public void setAndUpdateDisplayName(String name) {
        this.setDisplayName(name);
        PropertyChangeEvent event = new PropertyChangeEvent(this);
        event.setProperty(PropertyChangeEvent.DISPLAY_NAME, this.displayName);
        firePropertyChangeEvent(event);
    }
   
   
    /**
     * Sets the coomponent's owner and updates all views.
     *
     * @param name the owner name
     */
    public void setAndUpdateOwner(String name) {
        this.setOwner(name);
        PropertyChangeEvent event = new PropertyChangeEvent(this);
        event.setProperty(PropertyChangeEvent.OWNER, this.owner);
        firePropertyChangeEvent(event);
    }
   
    private void firePropertyChangeEvent(final PropertyChangeEvent event) {
        final Set<View> guiComponents = getAllViewManifestations();
        if (guiComponents != null) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    for (View gui : guiComponents) {
                        gui.updateMonitoredGUI(event);
                    }
                }
            });
        }
    }

   
    /**
     * Open this component in a new top level window.
     */
    public final void open() {
        Platform platform = PlatformAccess.getPlatform();
        assert platform != null;
       
        if (DetectGraphicsDevices.getInstance().getNumberGraphicsDevices() > DetectGraphicsDevices.MINIMUM_MONITOR_CHECK) {
            Frame frame = null;
            for (Frame f: Frame.getFrames()) {
                if (f.isActive() || f.isFocused()) {
                    frame = f;
                    break;
                }
            }
           
            if (frame != null) {           
                GraphicsConfiguration graphicsConfig = frame.getGraphicsConfiguration();
                open(graphicsConfig);
            } else {
                // Need this when MCT first startups and when there's no active window available.
                openInNewWindow(platform);
            }
           
        } else {
            openInNewWindow(platform);
        }
    }
   
    private void openInNewWindow(Platform platform) {
        assert platform != null : "Platform should not be null.";
        AbstractComponent ac = getComponentId() == null ? this : getNewComponentFromPersistence();
        if (ac != null) {
            platform.getWindowManager().openInNewWindow(ac);
        }
    }
   
    private AbstractComponent getNewComponentFromPersistence() {
        return PlatformAccess.getPlatform().getPersistenceProvider().getComponent(getComponentId());
    }
   
    /**
     * Detect multiple monitor displays and allow menu item to open this component in a new top level window.
     * @param graphicsConfig - Detect multiple display monitor devices
     */
    public final void open(GraphicsConfiguration graphicsConfig) {
        Platform platform = PlatformAccess.getPlatform();
        assert platform != null;
        WindowManager windowManager = platform.getWindowManager();
        if (platform.getRootComponent() == this)
            windowManager.openInNewWindow(this, graphicsConfig);
        else
            windowManager.openInNewWindow(PlatformAccess.getPlatform().getPersistenceProvider().getComponent(getComponentId()), graphicsConfig);
    }
   
    /**
     * Gets an instance of the capability. A capability is functionality that
     * can be provided by a component dynamically. For example, functionality
     * that can be provided only before a component has been initialized, doing this
     * using inheritance would require introducing an exception into the method
     * signatures and additional semantic documentation. The
     * capabilities provided are specific to the component type and are not
     * constrained by the platform.
     *
     * @param <T>
     *            Class of the capability
     * @param capability
     *            requested from the component
     * @return an instance of the capability requested or null if not provided
     */
    public final <T> T getCapability(Class<T> capability) {
        if (ComponentInitializer.class.isAssignableFrom(capability)) {
            if (initializer == null) {
                initializer = new Initializer();
            }
            return capability.cast(initializer);
        } else if (Updatable.class.isAssignableFrom(capability)) {
            if (initializer == null) {
                initializer = new Initializer();
            }
            return capability.cast(initializer);
        } else if (capability.isAssignableFrom(ComponentTypeInfo.class)) {
            if (typeInfo == null) {
                for (ComponentTypeInfo info :
                    ExternalComponentRegistryImpl.getInstance().getComponentInfos()) {
                    if (info != null && info.getComponentClass().equals(getClass())) {
                        typeInfo = info;
                        break;
                    }
                }
                if (typeInfo == null) {
                    typeInfo = new ComponentTypeInfo(getClass().getSimpleName(), getClass().getSimpleName(), getClass());
                }
            }
            return capability.cast(typeInfo);
        }
      
        return handleGetCapability(capability);
    }

    /**
     * Provides subclasses a chance to inject capabilities. The default
     * implementation does nothing by returning null. There are no requirements
     * on component providers to add capabilities.
     *
     * @param <T>
     *            Class of the capability
     * @param capability
     *            requested from the component
     * @see AbstractComponent#getCapability(Class)
     * @return an instance of the capability requested for null if not available
     */
    protected <T> T handleGetCapability(Class<T> capability) {
        return null;
    }

    /**
     * Provide multiple capabilities for a capability class.
     * @param <T> Class of the capability
     * @param capability requested from the component
     * @return list of capabilities
     */
     public final <T>List<T> getCapabilities(Class<T> capability) {
        return handleGetCapabilities(capability);
    }
   

    /**
     * Provides subclasses a chance to inject capabilities.
     * @param <T> the class of the capability
     * @param capability requested from the component
     * @return list of capabilities
     */
    protected <T>List<T> handleGetCapabilities(Class<T> capability) {
        T t = getCapability(capability);
        return t == null ? Collections.<T>emptyList() : Collections.singletonList(t);
    }
   
    private AbstractComponent getWorkUnitComponent() {
        return workUnitDelegate != null ? workUnitDelegate : this;
    }
   
    private void addComponentToWorkUnit() {
        PlatformAccess.getPlatform().getPersistenceProvider().addComponentToWorkUnit(getWorkUnitComponent());
    }
   
    /**
     * Determines if the underlying component has changes that only exist in memory.
     * @return true if the component needs to be saved false otherwise.
     */
    public boolean isDirty() {
        return getWorkUnitComponent().isDirty.get();
    }

    /**
     * Determines if the version of the underlying component is older than the component
     * persisted in the database.
     * @return true this version is older
     */
    public synchronized boolean isStale() {
        return isStale;
    }
    /**
     * Mark the component as having outstanding changes in memory.
     */
    public void save() {
        AbstractComponent workUnitComponent = getWorkUnitComponent();
        workUnitComponent.isDirty.set(true);
        addComponentToWorkUnit();
        if (workUnitComponent != this) {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    for (View v : viewManifestations)
                        v.updateMonitoredGUI();
                }
               
            });
           
            ObjectManager objectManager =
                            workUnitComponent.getCapability(ObjectManager.class);
            if (objectManager != null) {
                objectManager.addModifiedObject(this);
            }
        }
    }
   
    @Override
    public AbstractComponent clone() {
        try {
            Class<? extends AbstractComponent> componentClassType = this.getClass();
           
            String newID = IdGenerator.nextComponentId();
            AbstractComponent clonedComponent = componentClassType.newInstance();
            clonedComponent.setId(newID);
           
            ModelStatePersistence persistence = getCapability(ModelStatePersistence.class);
            if (persistence != null) {
                String modelState = persistence.getModelState();
                ModelStatePersistence clonedPersistence = clonedComponent.getCapability(ModelStatePersistence.class);
                assert clonedPersistence != null;
                clonedPersistence.setModelState(modelState);
               
            }

            for (AbstractComponent child : getComponents()) {
                clonedComponent.addComponent(child);
            }
           
           
            ensureViewPropertiesLoaded();
            clonedComponent.ensureViewPropertiesLoaded();
            for (Entry<String, ExtendedProperties> e : viewRoleProperties.entrySet()) {
                clonedComponent.viewRoleProperties.put(e.getKey(), e.getValue().clone());
            }

            clonedComponent.owner = owner;
            clonedComponent.creator = creator;
            clonedComponent.displayName = displayName;
            clonedComponent.externalKey = externalKey;
            clonedComponent.version = version;

            ComponentInitializer clonedCapability = clonedComponent.getCapability(ComponentInitializer.class);
           
            clonedCapability.initialize();
            return clonedComponent;
        } catch (SecurityException e) {
            throw new MCTRuntimeException(e);
        } catch (IllegalArgumentException e) {
            throw new MCTRuntimeException(e);
        } catch (InstantiationException e) {
            throw new MCTRuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new MCTRuntimeException(e);
        }
    }
  
    private synchronized void setViewProperty(String viewType, ExtendedProperties properties) {
        ensureViewPropertiesLoaded();
        this.viewRoleProperties.put(viewType, properties);
    }

    private synchronized Map<String,ExtendedProperties> getRawViewProperties() {
        return viewRoleProperties;
    }
   
    private synchronized Map<String,ExtendedProperties> getViewProperties() {
        ensureViewPropertiesLoaded();
        return viewRoleProperties;
    }
   
    private synchronized ExtendedProperties getViewProperties(String viewType) {
        ensureViewPropertiesLoaded();
        return this.viewRoleProperties.get(viewType);
    }

    /**
     * Returns the version of the component.
     * @return the version of the component
     */
    public int getVersion() {
        return version;
    }
   
    /**
     * Get an asset of a specified type. For instance, an
     * Icon may be retrieved using getAsset(Icon.class).
     * @param <T> the type of asset desired
     * @param assetClass the desired type of asset
     * @return an asset of the desired type (or null if there is none)
     */
    public <T> T getAsset(Class<T> assetClass) {
        return getCapability(ComponentTypeInfo.class).getAsset(assetClass);
    }
   
    /**
     * Returns the icon image for this component.
     * @return an icon image
     * @deprecated use getAsset(Icon.class) instead
     */
    @Deprecated
    public final javax.swing.ImageIcon getIcon() {
        return getAsset(javax.swing.ImageIcon.class);
    }
   
    /**
     * Returns the icon based on the component type.
     * @param className of the component type
     * @return an image icon
     */
    @Deprecated
    public static javax.swing.ImageIcon getIconForComponentType(String className) {
        for (ComponentTypeInfo componentTypeInfo :
            ExternalComponentRegistryImpl.getInstance().getComponentInfos()) {
            if (componentTypeInfo.getTypeClass().getName().equals(className)) {
                return componentTypeInfo.getAsset(javax.swing.ImageIcon.class);
            }
        }
        return null;
    }
   
    /**
     * Resets component properties.
     * @param txn the transaction to be performed atomically
     */
    public synchronized void resetComponentProperties(ResetPropertiesTransaction txn) {
        txn.perform();
    }
   
    /**
     * Adds a view manifestation that should be alerted to changes in this component.
     * @param viewManifestation to notify when changes occur.
     */
    public void addViewManifestation(View viewManifestation) {
        viewManifestations.add(viewManifestation);
    }
   
    /**
     * Gets all the currently monitored manifestations.
     * @return all the current views of this manifestation
     */
    public Set<View> getAllViewManifestations() {
        return new HashSet<View>(viewManifestations);
    }
   
    private final class Initializer implements ComponentInitializer, Updatable {
        private boolean initialized;
       
        @Override
        public void setId(String id) {
            AbstractComponent.this.id = id;
        }

        @Override
        public void setOwner(String owner) {
            AbstractComponent.this.owner = owner;
        }
       
        @Override
        public void setWorkUnitDelegate(AbstractComponent delegate) {
            AbstractComponent.this.workUnitDelegate = delegate;  
        }
       
        @Override
        public void componentSaved() {
            AbstractComponent.this.isDirty.set(false);
            AbstractComponent.this.resetOriginalOwner();
            AbstractComponent.this.releaseChildrenList();
        }
       
        @Override
        public void setViewRoleProperty(String viewRoleType, ExtendedProperties properties) {
            setViewProperty(viewRoleType, properties);
        }

        @Override
        public ExtendedProperties getViewRoleProperties(String viewType) {
            return getViewProperties(viewType);
        }

        @Override
        public void setCreationDate(Date creationDate) {
            AbstractComponent.this.creationDate = creationDate;
        }
       
        @Override
        public void setCreator(String creator) {
           AbstractComponent.this.creator = creator;
        }
       
        @Override
        public void addViewRoleProperties(String viewRoleType, ExtendedProperties properties) {
            addViewProperty(viewRoleType, properties);
        }
       
        @Override
        public void initialize() {
            checkInitialized();
            AbstractComponent.this.initialize();
        }

        public void setInitialized() {
            checkInitialized();
            initialized = true;
        }

        private void checkInitialized() {
            if (isInitialized()) {
                throw new IllegalStateException("component already initialized");
            }
        }

        @Override
        public boolean isInitialized() {
            return initialized;
        }

        @Override
        public Map<String, ExtendedProperties> getMutatedViewRoleProperties() {
            return AbstractComponent.this.getRawViewProperties();
        }
       
        @Override
        public Map<String, ExtendedProperties> getAllViewRoleProperties() {
            return Collections.unmodifiableMap(getViewProperties());
        }

        @Override
        public synchronized void setVersion(int version) {
            AbstractComponent.this.version = version;
            cachedComponentReferences.clear();
        }

        @Override
        public void setBaseDisplayedName(String baseDisaplyedName) {
            AbstractComponent.this.displayName = baseDisaplyedName;
        }

        @Override
        public void addReferences(List<AbstractComponent> references) {
            for (AbstractComponent c : references) {
                processAddDelegateComponent(-1, c);
            }
        }

        @Override
        public void removeReferences(List<AbstractComponent> references) {
            for (AbstractComponent c : references) {
                removeComponent(c);
            }
        }

        @Override
        public void notifyStale() {
            SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    for (View v : viewManifestations) {
                        v.notifyStaleState(true);
                    }
                }               
            });
        }

        @Override
        public synchronized void setStaleByVersion(int version) {
            if (getVersion() < version) {
                AbstractComponent.this.isStale = true ;
            }
        }
       
        @Override
        public synchronized void setStaleByVersion(String componentId, int version) {
            AbstractComponent component = findComponentById(componentId);
            if (component != null && component.getVersion() < version) {
                AbstractComponent.this.isStale = true ;
            }
        }       

        /* 
         * Search recursively for a component with a specific id. This is used
         * to support setStaleByVersion for work unit delegates. In principle this
         * search could take a long time, but in practice this will probably not be
         * the case as this should only be triggered when incoming changes occur
         * in a view which contains the specified components. The component's presence
         * in that view should generally imply that the component is not particularly
         * deep within the graph (hence the choice of a breadth-first search)
         */
        private AbstractComponent findComponentById(String id) {
            Set<String> ignore = new HashSet<String>();
            Queue<AbstractComponent> queue = new LinkedList<AbstractComponent>();
            queue.add(AbstractComponent.this);
            AbstractComponent toCheck;
            while ( (toCheck = queue.poll()) != null) {
                String checkedId = toCheck.getComponentId();
                if (checkedId.equals(id)) {
                    return toCheck;
                } else if (!ignore.contains(checkedId)) {
                    ignore.add(checkedId);
                    queue.addAll(toCheck.getComponents());
                }
            }
            return null;           
        }
    }   
   
    /**
     * Defines a transaction to reset component properties.
     */
    public interface ResetPropertiesTransaction {
        /**
         * Defines the steps to reset certain component properties.
         */
        public void perform();
    }
       
    /**
     * Get a list of all components to which this component refers.
     * Generally, a referenced component may be thought of as a child
     * of the referencing component, but the precise interpretation of the
     * relationship may vary among component types and view types.
     * @return a list of all referenced components
     */
    public synchronized List<AbstractComponent> getComponents() {
        return getOrLoadComponents();
    }

    /**
     * Returns the component by the specified id.
     * @param id of the component to find
     * @return component with the given id or null if no component currently has the id.
     */
    public static AbstractComponent getComponentById(String id) {
        Platform platform = PlatformAccess.getPlatform();
        return platform.getPersistenceProvider().getComponent(id);   
    }
   
    private void referencedComponentsMutated() {
        mutatedComponentReferences = cachedComponentReferences.get();
        assert mutatedComponentReferences != null : "method must be invoked while holding a strong reference to the mutated list of children";
    }
   
    private synchronized void releaseChildrenList() {
        mutatedComponentReferences = null;
    }
   
    private List<AbstractComponent> getOrLoadComponents() {
        if (isLeaf()) {
            return Collections.<AbstractComponent>emptyList();
        }
        List<AbstractComponent> currentlyReferencedComponents = cachedComponentReferences.get();
        if (currentlyReferencedComponents == null) {
            currentlyReferencedComponents = new ArrayList<AbstractComponent>(PlatformAccess.getPlatform().getPersistenceProvider().getReferencedComponents(this));
            cachedComponentReferences = new SoftReference<List<AbstractComponent>>(currentlyReferencedComponents);
        }
       
        return currentlyReferencedComponents;
    }
   
    /**
     * Add a reference to the specified component.
     * Generally, a referenced component may be thought of as a child
     * of the referencing component, but the precise interpretation of the
     * relationship may vary among component types and view types.
     * @param component the component to which to refer
     */
    private synchronized void addComponent(AbstractComponent component) {
        List<AbstractComponent> referencedComponents = getOrLoadComponents();
        referencedComponents.add(component);
        referencedComponentsMutated();
    }
   
    /**
     * Add a reference to the specified component, at the specified index.
     * Generally, a referenced component may be thought of as a child
     * of the referencing component, but the precise interpretation of the
     * relationship may vary among component types and view types.
     * @param index the index at which to reference
     * @param component the component to which to refer
     */
    private synchronized void addComponentAt(int index, AbstractComponent component) {
        List<AbstractComponent> referencedComponents = getOrLoadComponents();
        referencedComponents.add(index, component);
        referencedComponentsMutated();
    }
   
    /**
     * Remove a reference to the specified component
     * Generally, a referenced component may be thought of as a child
     * of the referencing component, but the precise interpretation of the
     * relationship may vary among component types and view types.
     * @param component the component to dereference
     */
    private synchronized void removeComponent(AbstractComponent component) {
        List<AbstractComponent> referencedComponents = getOrLoadComponents();
        for (AbstractComponent ac : referencedComponents) {
            if (ac.getComponentId().equals(component.getComponentId())) {
                referencedComponents.remove(ac);
                break;
            }
        }
        referencedComponentsMutated();
    }
   
    /**
     * Returns a description of nuclear data that is inspectable.
     * This ordered list of fields is rendered in MCT Platform's InfoView.
     *
     * @return ordered list of property descriptors
     */
    public List<PropertyDescriptor> getFieldDescriptors() {
        return null;
    }

}
TOP

Related Classes of gov.nasa.arc.mct.components.AbstractComponent$ResetPropertiesTransaction

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.