/* $Id: ProfileConfiguration.java 17822 2010-01-12 18:47:46Z linus $
*****************************************************************************
* Copyright (c) 2009 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* thn
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 2007 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.kernel;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
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 org.apache.log4j.Logger;
import org.argouml.application.events.ArgoEventPump;
import org.argouml.application.events.ArgoEventTypes;
import org.argouml.application.events.ArgoProfileEvent;
import org.argouml.configuration.Configuration;
import org.argouml.configuration.ConfigurationKey;
import org.argouml.model.Model;
import org.argouml.profile.DefaultTypeStrategy;
import org.argouml.profile.FigNodeStrategy;
import org.argouml.profile.FormatingStrategy;
import org.argouml.profile.Profile;
import org.argouml.profile.ProfileException;
import org.argouml.profile.ProfileFacade;
/**
* This class captures represents the unique access point for the
* configurability allowed by the use of profiles.
*
* @author maurelio1234
*/
public class ProfileConfiguration extends AbstractProjectMember {
/**
* Logger.
*/
private static final Logger LOG = Logger
.getLogger(ProfileConfiguration.class);
private FormatingStrategy formatingStrategy;
private DefaultTypeStrategy defaultTypeStrategy;
private List figNodeStrategies = new ArrayList();
private List<Profile> profiles = new ArrayList<Profile>();
private List<Object> profileModels = new ArrayList<Object>();
/**
* The extension used in serialization and returned by {@link #getType()}
*/
public static final String EXTENSION = "profile";
/**
* The configuration key for the default stereotype view.
*/
public static final ConfigurationKey KEY_DEFAULT_STEREOTYPE_VIEW =
Configuration.makeKey("profiles", "stereotypeView");
/**
* The default constructor for this class. Sets the default profiles as
* given by {@link org.argouml.profile.ProfileManager} as the profiles of
* the project.
*
* @param project the project that contains this configuration
*/
public ProfileConfiguration(Project project) {
super(EXTENSION, project);
List c = project.getUserDefinedModelList();
Object m = c.isEmpty() ? null : c.get(0);
if (project.getProjectType() != Project.PROFILE_PROJECT) {
// a profile initially has no applied profiles, we go bottom-up
for (Profile p : ProfileFacade.getManager().getDefaultProfiles()) {
addProfile(p, m);
}
}
updateStrategies();
}
/**
* The constructor for pre-defined profile configurations, such as when a
* project is read from a saved file.
* @param project the project that contains this configuration
* @param configuredProfiles the {@link Profile}s that will be the project
* profiles
*/
public ProfileConfiguration(Project project,
Collection<Profile> configuredProfiles) {
super(EXTENSION, project);
List c = project.getUserDefinedModelList();
Object m = c.isEmpty() ? null : c.get(0);
for (Profile profile : configuredProfiles) {
addProfile(profile, m);
}
updateStrategies();
}
private void updateStrategies() {
for (Profile profile : profiles) {
activateFormatingStrategy(profile);
activateDefaultTypeStrategy(profile);
}
}
/**
* @return the current formating strategy
*/
public FormatingStrategy getFormatingStrategy() {
return formatingStrategy;
}
/**
* @return the current default type strategy
*/
public DefaultTypeStrategy getDefaultTypeStrategy() {
return defaultTypeStrategy;
}
/**
* Updates the current strategy to the strategy provided by the
* passed profile. The profile should have been previously registered.
*
* @param profile the profile providing the current default type strategy
*/
public void activateDefaultTypeStrategy(Profile profile) {
if (profile != null && profile.getDefaultTypeStrategy() != null
&& getProfiles().contains(profile)) {
this.defaultTypeStrategy = profile.getDefaultTypeStrategy();
}
}
/**
* Updates the current strategy to the strategy provided by the
* passed profile. The profile should have been previously registered.
*
* @param profile the profile providing the current formating strategy
*/
public void activateFormatingStrategy(Profile profile) {
if (profile != null && profile.getFormatingStrategy() != null
&& getProfiles().contains(profile)) {
this.formatingStrategy = profile.getFormatingStrategy();
}
}
/**
* @return the list of applied profiles
*/
public List<Profile> getProfiles() {
return profiles;
}
/**
* Applies a new profile to this configuration.
*
* @param p the profile to be applied
* @deprecated for 0.29.2, because since UML2 a profile must be applied to
* a model
*/
@Deprecated
public void addProfile(Profile p) {
addProfile(p, null);
}
/**
* Applies a new profile to this configuration and to the given model (or
* other profile, which could be later a collection).
*
* @param p the profile to be applied
* @param m the model (or profile) to which the profile will be applied
*/
@SuppressWarnings("unchecked")
public void addProfile(Profile p, Object m) {
if (!profiles.contains(p)) {
profiles.add(p);
try {
for (Object profile : p.getProfilePackages()) {
Model.getExtensionMechanismsHelper().applyProfile(m, profile);
}
profileModels.addAll(p.getProfilePackages());
} catch (ProfileException e) {
LOG.warn("Error retrieving profile's " + p + " packages.", e);
}
FigNodeStrategy fns = p.getFigureStrategy();
if (fns != null) {
figNodeStrategies.add(fns);
}
for (Profile dependency : p.getDependencies()) {
addProfile(dependency, m);
}
updateStrategies();
ArgoEventPump.fireEvent(new ArgoProfileEvent(
ArgoEventTypes.PROFILE_ADDED, new PropertyChangeEvent(this,
"profile", null, p)));
}
}
/**
* @return the list of models of the currently applied profile.
*/
private List getProfileModels() {
return profileModels;
}
/**
* Removes the passed profile from this configuration.
*
* @param p the profile to be applied
* @deprecated for 0.29.2, because since UML2 a profile must be unapplied
* from a model
*/
@Deprecated
public void removeProfile(Profile p) {
removeProfile(p, null);
}
/**
* Removes the passed profile from the configuration and unapplies it from
* the given model (or other profile, which could be later a collection).
*
* @param p the profile to be removed/unapplied
* @param m the model (or profile) to which the profile will be unapplied
*/
public void removeProfile(Profile p, Object m) {
profiles.remove(p);
try {
for (Object profile : p.getProfilePackages()) {
Model.getExtensionMechanismsHelper().unapplyProfile(m, profile);
}
profileModels.removeAll(p.getProfilePackages());
} catch (ProfileException e) {
LOG.error("Exception", e);
}
FigNodeStrategy fns = p.getFigureStrategy();
if (fns != null) {
figNodeStrategies.remove(fns);
}
if (formatingStrategy == p.getFormatingStrategy()) {
formatingStrategy = null;
}
List<Profile> markForRemoval = new ArrayList<Profile>();
for (Profile profile : profiles) {
if (profile.getDependencies().contains(p)) {
markForRemoval.add(profile);
}
}
for (Profile profile : markForRemoval) {
removeProfile(profile, m);
}
updateStrategies();
ArgoEventPump.fireEvent(new ArgoProfileEvent(
ArgoEventTypes.PROFILE_REMOVED, new PropertyChangeEvent(this,
"profile", p, null)));
}
private FigNodeStrategy compositeFigNodeStrategy = new FigNodeStrategy() {
public Image getIconForStereotype(Object element) {
Iterator it = figNodeStrategies.iterator();
while (it.hasNext()) {
FigNodeStrategy strat = (FigNodeStrategy) it.next();
Image extra = strat.getIconForStereotype(element);
if (extra != null) {
return extra;
}
}
return null;
}
};
/**
* @return the current FigNodeStrategy
*/
public FigNodeStrategy getFigNodeStrategy() {
return compositeFigNodeStrategy;
}
/**
* @return the extension for this project member
* @see org.argouml.kernel.AbstractProjectMember#getType()
*/
public String getType() {
return EXTENSION;
}
/**
* Objects of this class are always consistent, there's no need
* to repair them.
*
* @return the empty string.
* @see org.argouml.kernel.ProjectMember#repair()
*/
public String repair() {
return "";
}
/**
* @return the "Profile Configuration" string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Profile Configuration";
}
/**
* Find a stereotype with the given name which is applicable to the given
* element.
*
* @param name name of stereotype to look for
* @param element model element to which the stereotype must be applicable
* @return the stereotype or null if none found
*/
public Object findStereotypeForObject(String name, Object element) {
Iterator iter = null;
for (Object model : profileModels) {
iter = Model.getFacade().getOwnedElements(model).iterator();
while (iter.hasNext()) {
Object stereo = iter.next();
if (!Model.getFacade().isAStereotype(stereo)
|| !name.equals(Model.getFacade().getName(stereo))) {
continue;
}
if (Model.getExtensionMechanismsHelper().isValidStereotype(
element, stereo)) {
return stereo;
}
}
}
return null;
}
/**
* Search for the given type in all of the profile models.
*
* @param name name of type to be found
* @return the type or null
*/
public Object findType(String name) {
for (Object model : getProfileModels()) {
Object result = findTypeInModel(name, model);
if (result != null) {
return result;
}
}
return null;
}
/**
* Finds a type in a model by name
*
* FIXME: duplicated from the method with the same name in
* org.argouml.profile.internal.ModelUtils.
*
* @param s the type name
* @param model the model
* @return the type or <code>null</code> if the type has not been found.
*/
public static Object findTypeInModel(String s, Object model) {
if (!Model.getFacade().isANamespace(model)) {
throw new IllegalArgumentException(
"Looking for the classifier " + s
+ " in a non-namespace object of " + model
+ ". A namespace was expected.");
}
Collection allClassifiers =
Model.getModelManagementHelper()
.getAllModelElementsOfKind(model,
Model.getMetaTypes().getClassifier());
Object[] classifiers = allClassifiers.toArray();
Object classifier = null;
for (int i = 0; i < classifiers.length; i++) {
classifier = classifiers[i];
if (Model.getFacade().getName(classifier) != null
&& Model.getFacade().getName(classifier).equals(s)) {
return classifier;
}
}
return null;
}
/**
* Find all the model elements in the configured {@link Profile}s
* of the given meta type.
*
* @param metaType the meta type of the model elements to find
* @return a {@link Collection} containing the model elements that
* are of the given meta type
*/
@SuppressWarnings("unchecked")
public Collection findByMetaType(Object metaType) {
Set elements = new HashSet();
Iterator it = getProfileModels().iterator();
while (it.hasNext()) {
Object model = it.next();
elements.addAll(Model.getModelManagementHelper()
.getAllModelElementsOfKind(model, metaType));
}
return elements;
}
/**
* @param modelElement
* ModelElement for which find possible stereotypes
* @return collection of stereotypes which are valid for the given model
* element.
*/
public Collection findAllStereotypesForModelElement(Object modelElement) {
return Model.getExtensionMechanismsHelper().getAllPossibleStereotypes(
getProfileModels(), modelElement);
}
}