Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/uml/StereotypeUtility.java
// $Id: StereotypeUtility.java 16176 2008-11-26 05:02:06Z tfmorris $
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

package org.argouml.uml;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.Action;

import org.argouml.kernel.Project;
import org.argouml.kernel.ProjectManager;
import org.argouml.model.Model;
import org.argouml.uml.util.PathComparator;
import org.argouml.util.MyTokenizer;

/**
 * Utility classes for use in diagram popup menus for stereotypes.
 * 
 * @author Bob Tarling
 *
 */
public class StereotypeUtility {

    /**
     * Private default constructor.
     */
    private StereotypeUtility() {
        super();
    }

    public static Action[] getApplyStereotypeActions(Object modelElement) {
        Set availableStereotypes = getAvailableStereotypes(modelElement);
        
        if (!availableStereotypes.isEmpty()) {
            Action[] menuActions = new Action[availableStereotypes.size()];

            Iterator it = availableStereotypes.iterator();
            for (int i = 0; it.hasNext(); ++i) {
                menuActions[i] = new ActionAddStereotype(modelElement, 
                        it.next());
            }
            return menuActions;
        }
        return new Action[0];
    }

    /**
     * Returns a set of all unique applicable stereotypes 
     * for a given modelelement.
     * 
     * @param modelElement the given modelelement
     * @return the set with stereotype UML objects
     */
    public static Set<Object> getAvailableStereotypes(Object modelElement) {
        Set<List> paths = new HashSet<List>();
        Set<Object> availableStereotypes = 
            new TreeSet<Object>(new PathComparator());
        Collection models =
            ProjectManager.getManager().getCurrentProject().getModels();
        
        Collection topLevelModels =
            ProjectManager.getManager().getCurrentProject().getModels();
        
        // adds all stereotypes defined at the top level namespaces        
        Collection topLevelStereotypes = getTopLevelStereotypes(topLevelModels);
        
        Collection validTopLevelStereotypes = new ArrayList();
            
        addAllUniqueModelElementsFrom(availableStereotypes, paths, Model
                .getExtensionMechanismsHelper().getAllPossibleStereotypes(
                        models, modelElement));
        for (Object stereotype : topLevelStereotypes) {
            if (Model.getExtensionMechanismsHelper().isValidStereotype(
                    modelElement, stereotype)) {
                validTopLevelStereotypes.add(stereotype);
            }            
        }

        addAllUniqueModelElementsFrom(availableStereotypes, paths,
                validTopLevelStereotypes);
        
        // adds all stereotypes defined at the profiles applied to the
        // current namespace
        Object namespace = Model.getFacade().getNamespace(modelElement);
        if (namespace != null) {
            while (true) {
                getApplicableStereotypesInNamespace(modelElement, paths,
                        availableStereotypes, namespace);
                Object newNamespace = Model.getFacade().getNamespace(namespace);

                if (newNamespace == null) {
                    break;
                }

                namespace = newNamespace;
            }
        }
        
        // adds all stereotypes defined at the profiles applied 
        // to the current project
        addAllUniqueModelElementsFrom(availableStereotypes, paths,
                ProjectManager.getManager().getCurrentProject()
                        .getProfileConfiguration()
                        .findAllStereotypesForModelElement(modelElement));
        
        return availableStereotypes;
    }

    private static Collection<Object> getTopLevelStereotypes(
            Collection<Object> topLevelModels) {
        Collection<Object> ret = new ArrayList<Object>();
        for (Object model : topLevelModels) {
            for (Object stereotype : Model.getExtensionMechanismsHelper()
                    .getStereotypes(model)) {
                Object namespace = Model.getFacade().getNamespace(stereotype);
                if (Model.getFacade().getNamespace(namespace) == null) {
                    ret.add(stereotype);
                }
            }
        }
        return ret;
    }
    
    private static void getApplicableStereotypesInNamespace(
            Object modelElement, Set<List> paths,
            Set<Object> availableStereotypes, Object namespace) {
        
        Collection allProfiles = getAllProfilePackages(Model.getFacade()
                .getModel(modelElement));
        Collection<Object> allAppliedProfiles = new ArrayList<Object>();
        
        for (Object profilePackage : allProfiles) {
            Collection allDependencies = Model.getCoreHelper().getDependencies(
                    profilePackage, namespace);

            for (Object dependency : allDependencies) {
                if (Model.getExtensionMechanismsHelper().hasStereotype(
                        dependency, "appliedProfile")) {
                    allAppliedProfiles.add(profilePackage);
                    break;
                }
            }
        }
        
        addAllUniqueModelElementsFrom(availableStereotypes, paths,
                getApplicableStereotypes(modelElement, allAppliedProfiles));
    }
    
    private static Collection<Object> getApplicableStereotypes(
            Object modelElement, Collection<Object> allAppliedProfiles) {
        Collection<Object> ret = new ArrayList<Object>();
        for (Object profile : allAppliedProfiles) {
            for (Object stereotype : Model.getExtensionMechanismsHelper()
                    .getStereotypes(profile)) {
                if (Model.getExtensionMechanismsHelper().isValidStereotype(
                        modelElement, stereotype)) {
                    ret.add(stereotype);
                }
            }
        }

        return ret;
    }

    private static Collection<Object> getAllProfilePackages(Object model) {
        Collection col = Model.getModelManagementHelper()
                .getAllModelElementsOfKind(model,
                        Model.getMetaTypes().getPackage());
        Collection<Object> ret = new ArrayList<Object>();
        
        for (Object element : col) {
            if (Model.getFacade().isAPackage(element)
                    && Model.getExtensionMechanismsHelper().hasStereotype(
                            element, "profile")) {
                ret.add(element);
            }
        }
        return ret;
    }

    /**
     * Helper method for buildModelList.
     * <p>
     * Adds those elements from source that do not have the same path as any
     * path in paths to elements, and its path to paths. Thus elements will
     * never contain two objects with the same path, unless they are added by
     * other means.
     */
    private static void addAllUniqueModelElementsFrom(Set<Object> elements,
            Set<List> paths, Collection<Object> source) {
        for (Object obj : source) {
            List path = Model.getModelManagementHelper().getPathList(obj);
            if (!paths.contains(path)) {
                paths.add(path);
                elements.add(obj);
            }
        }
    }

    /**
     * Replace the previous set of stereotypes applied to the given modelelement
     * with a new set, given in the form of a "," separated string of stereotype
     * names.
     * 
     * @param element the UML element to modify
     * @param stereotype Comma separated list of stereotype names. Empty string
     *                or <code>null</code> represents no stereotypes.
     * @param removeCurrent true if all current stereotypes should be removed
     *                before adding the new stereotypes, false if new
     *                stereotypes should be added to existing ones.
     */
    public static void dealWithStereotypes(Object element,
            StringBuilder stereotype, boolean removeCurrent) {
        if (stereotype == null) {
            dealWithStereotypes(element, (String) null, removeCurrent);
        } else {
            dealWithStereotypes(element, stereotype.toString(), removeCurrent);
        }
    }

    /**
     * This function shall replace the previous set of stereotypes of the given
     * modelelement with a new set, given in the form of a "," separated string
     * of stereotype names.
     * 
     * @param umlobject the UML element to adapt
     * @param stereotype Comma separated list stereotype names. Empty string or
     *                <code>null</code> represents no stereotypes.
     * @param full false if stereotypes are only added, true if removal should
     *                be done, too.
     */
    public static void dealWithStereotypes(Object umlobject, String stereotype,
            boolean full) {
        String token;
        MyTokenizer mst;
        Collection<String> stereotypes = new ArrayList<String>();

        /* Convert the string (e.g. "aaa,bbb,ccc")
         * into separate stereotype-names (e.g. "aaa", "bbb", "ccc").
         */
        if (stereotype != null) {
            mst = new MyTokenizer(stereotype, " ,\\,");
            while (mst.hasMoreTokens()) {
                token = mst.nextToken();
                if (!",".equals(token) && !" ".equals(token)) {
                    stereotypes.add(token);
                }
            }
        }

        if (full) {
            // collect the to be removed stereotypes
            Collection<Object> toBeRemoved = new ArrayList<Object>();
            for (Object stereo : Model.getFacade().getStereotypes(umlobject)) {
                String stereotypename = Model.getFacade().getName(stereo);
                if (stereotypename != null
                        && !stereotypes.contains(stereotypename)) {
                    toBeRemoved.add(getStereotype(umlobject, stereotypename));
                }
            }

            // and now remove them
            for (Object o : toBeRemoved) {
                Model.getCoreHelper().removeStereotype(umlobject, o);
            }
        }

        // add stereotypes
        for (String stereotypename : stereotypes) {
            if (!Model.getExtensionMechanismsHelper()
                    .hasStereotype(umlobject, stereotypename)) {
                Object umlstereo = getStereotype(umlobject, stereotypename);
                if (umlstereo != null) {
                    Model.getCoreHelper().addStereotype(umlobject, umlstereo);
                }
            }
        }
    }

    /**
     * Finds a stereotype with the given name either in the user model, or in
     * one of the profiles' models.  If it's not found, a new stereotype will
     * be created in the root model.
     * 
     * @param obj A ModelElement to find a suitable stereotype for.
     * @param name The name of the stereotype to search for.
     * @return A stereotype named name, or possibly null.
     */
    private static Object getStereotype(Object obj, String name) {
        Object root = Model.getFacade().getModel(obj);
        Object stereo;

        stereo = findStereotypeContained(obj, root, name);
        // TODO: The following rather than the above is probably the correct
        // way to search
//        stereo = findStereotype(obj, null, name);
        if (stereo != null) {
            return stereo;
        }

        Project project = ProjectManager.getManager().getCurrentProject();
        stereo = project.getProfileConfiguration().findStereotypeForObject(
                name, obj); 

        if (stereo != null) {
            return stereo;
        }

        if (root != null && name.length() > 0) {
            stereo =
                Model.getExtensionMechanismsFactory().buildStereotype(
                    obj, name, root);
        }

        return stereo;
    }

    /**
     * Search for a stereotype with the name given in a namespace and its
     * containing namespaces.
     * 
     * @param obj The model element to be suitable for.
     * @param namespace The namespace to start search at. If null, the namespace
     *                of the given model element will be used as the starting
     *                point.
     * @param name The name of the stereotype to search for.
     * @return An stereotype named name, or null if none is found.
     */
    private static Object findStereotype(
            final Object obj, final Object namespace, final String name) {
        Object ns = namespace;
        if (ns == null) {
            ns = Model.getFacade().getNamespace(obj);
            if (ns == null) {
                return null;
            }
        }

        
        Collection ownedElements = 
            Model.getFacade().getOwnedElements(ns);
        for (Object element : ownedElements) {
            if (Model.getFacade().isAStereotype(element)
                    && name.equals(Model.getFacade().getName(element))) {
                return element;
            }
        }
        
        // If not found, try the parent namespace
        ns = Model.getFacade().getNamespace(ns);
        if (namespace != null) {
            return findStereotype(obj, ns, name);
        }

        return null;
    }
    
    /**
     * Search descending recursively for a stereotype with the name given
     * in name.  NOTE: You probably don't want to use this because it's 
     * searching the wrong direction!
     *
     * @param obj
     *            The model element to be suitable for.
     * @param root
     *            The model element to search from.
     * @param name
     *            The name of the stereotype to search for.
     * @return An stereotype named name, or null if none is found.
     */
    private static Object findStereotypeContained(
            Object obj, Object root, String name) {
        Object stereo;

        if (root == null) {
            return null;
        }
        
        if (Model.getFacade().isAStereotype(root)
                && name.equals(Model.getFacade().getName(root))) {
            if (Model.getExtensionMechanismsHelper().isValidStereotype(obj,
                    root)) {
                return root;
            }
        }

        if (!Model.getFacade().isANamespace(root)) {
            return null;
        }

        Collection ownedElements = Model.getFacade().getOwnedElements(root);

        // Loop through each element in the namespace, recursing.
        for (Object ownedElement : ownedElements) {
            stereo = findStereotypeContained(obj, ownedElement, name);
            if (stereo != null) {
                return stereo;
            }
        }
        return null;
    }

}
