/* $Id: ProfileUML.java 18925 2010-12-19 08:57:33Z thn $
*****************************************************************************
* Copyright (c) 2008,2010 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:
* maurelio1234 - Initial implementation
* thn
* Tom Morris - lazy loading
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2009 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.profile.internal;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.log4j.Logger;
import org.argouml.cognitive.Critic;
import org.argouml.cognitive.ToDoItem;
import org.argouml.cognitive.Translator;
import org.argouml.model.Model;
import org.argouml.profile.CoreProfileReference;
import org.argouml.profile.DefaultTypeStrategy;
import org.argouml.profile.FormatingStrategy;
import org.argouml.profile.Profile;
import org.argouml.profile.ProfileException;
import org.argouml.profile.ProfileModelLoader;
import org.argouml.profile.ProfileReference;
import org.argouml.profile.ResourceModelLoader;
import org.argouml.profile.internal.ocl.CrOCL;
import org.argouml.profile.internal.ocl.InvalidOclException;
import org.argouml.uml.cognitive.critics.CrAssocNameConflict;
import org.argouml.uml.cognitive.critics.CrAttrNameConflict;
import org.argouml.uml.cognitive.critics.CrCircularAssocClass;
import org.argouml.uml.cognitive.critics.CrCircularInheritance;
import org.argouml.uml.cognitive.critics.CrClassMustBeAbstract;
import org.argouml.uml.cognitive.critics.CrCrossNamespaceAssoc;
import org.argouml.uml.cognitive.critics.CrDupParamName;
import org.argouml.uml.cognitive.critics.CrDupRoleNames;
import org.argouml.uml.cognitive.critics.CrFinalSubclassed;
import org.argouml.uml.cognitive.critics.CrForkOutgoingTransition;
import org.argouml.uml.cognitive.critics.CrIllegalGeneralization;
import org.argouml.uml.cognitive.critics.CrInterfaceAllPublic;
import org.argouml.uml.cognitive.critics.CrInterfaceOperOnly;
import org.argouml.uml.cognitive.critics.CrInvalidBranch;
import org.argouml.uml.cognitive.critics.CrInvalidFork;
import org.argouml.uml.cognitive.critics.CrInvalidHistory;
import org.argouml.uml.cognitive.critics.CrInvalidInitial;
import org.argouml.uml.cognitive.critics.CrInvalidJoin;
import org.argouml.uml.cognitive.critics.CrInvalidJoinTriggerOrGuard;
import org.argouml.uml.cognitive.critics.CrInvalidPseudoStateTrigger;
import org.argouml.uml.cognitive.critics.CrInvalidSynch;
import org.argouml.uml.cognitive.critics.CrJoinIncomingTransition;
import org.argouml.uml.cognitive.critics.CrMultiComposite;
import org.argouml.uml.cognitive.critics.CrMultipleAgg;
import org.argouml.uml.cognitive.critics.CrMultipleDeepHistoryStates;
import org.argouml.uml.cognitive.critics.CrMultipleShallowHistoryStates;
import org.argouml.uml.cognitive.critics.CrNWayAgg;
import org.argouml.uml.cognitive.critics.CrNameConflict;
import org.argouml.uml.cognitive.critics.CrNameConflictAC;
import org.argouml.uml.cognitive.critics.CrNameConfusion;
import org.argouml.uml.cognitive.critics.CrOppEndConflict;
import org.argouml.uml.cognitive.critics.CrOppEndVsAttr;
/**
* This class represents the default UML profile
*
* @author maurelio1234
*/
public class ProfileUML extends Profile {
private static final Logger LOG = Logger.getLogger(ProfileUML.class);
private static final String PROFILE_UML14_FILE = "default-uml14.xmi";
private static final String PROFILE_UML22_FILE = "default-uml22.xmi";
static final String NAME_UML14 = "UML 1.4";
static final String NAME_UML22 = "UML 2.2";
private FormatingStrategy formatingStrategy;
private ProfileModelLoader profileModelLoader;
private Collection model;
private Set<Critic> critics = null;
private ProfileReference profileReference = null;
/**
* Construct a Profile for UML modeling.
* @throws ProfileException
*/
@SuppressWarnings("unchecked")
ProfileUML() throws ProfileException {
formatingStrategy = new FormatingStrategyUML();
try {
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
profileReference =
new CoreProfileReference(PROFILE_UML14_FILE);
} else {
//TODO: reference should be handled better
CoreProfileReference.setProfileDirectory("uml22");
profileReference =
new CoreProfileReference(PROFILE_UML22_FILE);
}
} catch (MalformedURLException e) {
throw new ProfileException(
"Exception while creating profile reference.", e);
}
}
private Collection getModel() {
if (model == null) {
profileModelLoader = new ResourceModelLoader();
try {
model = profileModelLoader.loadModel(profileReference);
} catch (ProfileException e) {
LOG.error("Error loading UML profile", e);
}
if (model == null) {
model = new ArrayList();
model.add(Model.getModelManagementFactory().createProfile());
}
}
return model;
}
private void loadWellFormednessRules() {
critics = new HashSet<Critic>();
critics.add(new CrAssocNameConflict());
critics.add(new CrAttrNameConflict());
critics.add(new CrCircularAssocClass());
critics.add(new CrCircularInheritance());
critics.add(new CrClassMustBeAbstract());
critics.add(new CrCrossNamespaceAssoc());
critics.add(new CrDupParamName());
critics.add(new CrDupRoleNames());
critics.add(new CrNameConfusion());
critics.add(new CrInvalidHistory());
critics.add(new CrInvalidSynch());
critics.add(new CrInvalidJoinTriggerOrGuard());
critics.add(new CrInvalidPseudoStateTrigger());
critics.add(new CrInvalidInitial());
critics.add(new CrInvalidJoin());
critics.add(new CrInvalidFork());
critics.add(new CrInvalidBranch());
critics.add(new CrMultipleDeepHistoryStates());
critics.add(new CrMultipleShallowHistoryStates());
critics.add(new CrForkOutgoingTransition());
critics.add(new CrJoinIncomingTransition());
critics.add(new CrFinalSubclassed());
critics.add(new CrIllegalGeneralization());
critics.add(new CrInterfaceAllPublic());
critics.add(new CrInterfaceOperOnly());
critics.add(new CrMultipleAgg());
critics.add(new CrNWayAgg());
critics.add(new CrNameConflictAC());
critics.add(new CrOppEndConflict());
critics.add(new CrMultiComposite());
critics.add(new CrNameConflict());
critics.add(new CrOppEndVsAttr());
// Missing WFRs
// Association Class
// 4.5.3.2 [1]
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context AssociationClass inv:"
+ "self.allConnections->"
+ "forAll( ar | self.allFeatures->"
+ "forAll( f | f.oclIsKindOf(StructuralFeature) "
+ "implies ar.name <> f.name ))",
Translator.localize("wfr.UML142.AssociationClass.1-head"),
Translator.localize("wfr.UML142.AssociationClass.1-desc"),
ToDoItem.HIGH_PRIORITY, null, null, "http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// 4.5.3.2 [2]
/* Testing: Works Ok. */
try {
critics.add(new CrOCL("context AssociationClass inv:"
+ "self.allConnections->"
+ "forAll(ar | ar.participant <> self)",
Translator.localize("wfr.UML142.AssociationClass.2-head"),
Translator.localize("wfr.UML142.AssociationClass.2-desc"),
ToDoItem.HIGH_PRIORITY, null, null, "http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// Behavioral Feature
// 4.5.3.5 [2]
// it works, but a bug in namespace.contents prevents it from
// working when the type of the parameter comes from a profile
// try {
// Agency.register(new CrOCL("context BehavioralFeature inv:"
// + "self.parameter->"
// + "forAll( p | self.owner.namespace.allContents->"
// + "includes (p.type) )",
// "The type of the Parameters should be "
// + "included in the Namespace of the Classifier.",
// null, ToDoItem.HIGH_PRIORITY, null, null,
// "http://www.uml.org/"));
// } catch (InvalidOclException e) {
// e.printStackTrace();
// }
// Classifier
// 4.5.3.8 [5]
/* TODO: Partly overlaps CrOppEndVsAttr. */
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context Classifier inv:"
+ "self.oppositeAssociationEnds->"
+ "forAll( o | not self.allAttributes->"
+ "union (self.allContents)->"
+ "collect ( q | q.name )->includes (o.name) )",
Translator.localize("wfr.UML142.Classifier.5-head"),
Translator.localize("wfr.UML142.Classifier.5-desc"),
ToDoItem.HIGH_PRIORITY, null, null, "http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// DataType
// 4.5.3.12 [1]
/* Tested with fabricated XMI - OK. */
try {
critics.add(new CrOCL("context DataType inv:"
+ "self.allFeatures->forAll(f | f.oclIsKindOf(Operation)"
+ " and f.oclAsType(Operation).isQuery)",
Translator.localize("wfr.UML142.DataType.1-head"),
Translator.localize("wfr.UML142.DataType.1-desc"),
ToDoItem.HIGH_PRIORITY, null, null, "http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// GeneralizableElement
// 4.5.3.20 [1]
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context GeneralizableElement inv:"
+ "self.isRoot implies self.generalization->isEmpty",
Translator.localize("wfr.UML142.GeneralizableElement.1-head"),
Translator.localize("wfr.UML142.GeneralizableElement.1-desc"),
ToDoItem.HIGH_PRIORITY, null, null, "http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// 4.5.3.20 [4]
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context GeneralizableElement inv:"
+ "self.generalization->"
+ "forAll(g |self.namespace.allContents->"
+ "includes(g.parent) )",
Translator.localize("wfr.UML142.GeneralizableElement.4-head"),
Translator.localize("wfr.UML142.GeneralizableElement.4-desc"),
ToDoItem.HIGH_PRIORITY, null, null, "http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// Namespace
// 4.5.3.26 [2]
/* Testing: Does not fire. Conflict with CrNameConflict. */
try {
critics.add(new CrOCL("context Namespace inv:"
+ "self.allContents -> "
+ "select(x|x.oclIsKindOf(Association))->"
+ "forAll(a1, a2 |a1.name = a2.name and "
+ "a1.connection.participant = a2.connection.participant"
+ " implies a1 = a2)",
Translator.localize("wfr.UML142.Namespace.2-head"),
Translator.localize("wfr.UML142.Namespace.2-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// Actor
// 4.11.3.1 [1]
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context Actor inv: "
+ "self.associations->forAll(a | "
+ "a.connection->size = 2)",
Translator.localize("wfr.UML142.Actor.1a-head"),
Translator.localize("wfr.UML142.Actor.1a-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context Actor inv: "
+ "self.associations->forAll(a | "
// + "a.allConnections->exists(r | r.type.oclIsKindOf(Actor)) and "
+ "a.allConnections->exists(r | "
+ "r.type.oclIsKindOf(UseCase) or "
+ "r.type.oclIsKindOf(Subsystem) or "
+ "r.type.oclIsKindOf(Class)))",
Translator.localize("wfr.UML142.Actor.1b-head"),
Translator.localize("wfr.UML142.Actor.1b-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// Actor
// 4.11.3.1 [2]
/* Tested with fabricated XMI - OK. */
try {
critics.add(new CrOCL("context Actor inv:"
+ "self.contents->isEmpty",
Translator.localize("wfr.UML142.Actor.2-head"),
Translator.localize("wfr.UML142.Actor.2-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// UseCase
// 4.11.3.5 [1]
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context UseCase inv:"
+ "self.associations->forAll(a | a.connection->size = 2)",
Translator.localize("wfr.UML142.UseCase.1-head"),
Translator.localize("wfr.UML142.UseCase.1-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// UseCase
// 4.11.3.5 [2]
/* Testing: does not fire. */
try {
critics.add(new CrOCL("context UseCase inv:"
+ "self.associations->forAll(a | "
+ "a.allConnections->forAll(s, o| "
+ "(s.type.specificationPath->isEmpty and "
+ "o.type.specificationPath->isEmpty ) "
+ "or "
+ "(not s.type.specificationPath->includesAll( "
+ "o.type.specificationPath) and "
+ "not o.type.specificationPath->includesAll( "
+ "s.type.specificationPath)) "
+ "))",
Translator.localize("wfr.UML142.UseCase.2-head"),
Translator.localize("wfr.UML142.UseCase.2-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// UseCase
// 4.11.3.5 [3]
/* Tested with fabricated XMI - OK. */
try {
critics.add(new CrOCL("context UseCase inv:"
+ "self.contents->isEmpty",
Translator.localize("wfr.UML142.UseCase.3-head"),
Translator.localize("wfr.UML142.UseCase.3-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// UseCase
// 4.11.3.5 [4]
/* Tested OK, except in some cases, depending on the
* sequence of the EPs. Probably the implementation of
* "forAll (x, y | ..." does not cover all combinations. */
try {
critics.add(new CrOCL("context UseCase inv:"
+ "self.allExtensionPoints -> forAll (x, y | "
+ "x.name = y.name implies x = y )",
Translator.localize("wfr.UML142.UseCase.4-head"),
Translator.localize("wfr.UML142.UseCase.4-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
// ActionState
// 4.13.3.2 [3]
// Issue 715
try {
critics.add(new CrOCL("context ActionState inv:"
+ "self.outgoing->forAll(t | t.trigger->size = 0)",
Translator.localize("wfr.UML142.ActionState.3-head"),
Translator.localize("wfr.UML142.ActionState.3-desc"),
ToDoItem.HIGH_PRIORITY, null, null,
"http://www.uml.org/"));
} catch (InvalidOclException e) {
e.printStackTrace();
}
setCritics(critics);
}
@Override
public FormatingStrategy getFormatingStrategy() {
return formatingStrategy;
}
@Override
public String getDisplayName() {
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
return NAME_UML14;
}
return NAME_UML22;
}
@Override
public Set<Critic> getCritics() {
if (critics == null) {
loadWellFormednessRules();
}
return super.getCritics();
}
@Override
public Collection getProfilePackages() {
return Collections.unmodifiableCollection(getModel());
}
@Override
public Collection<Object> getLoadedPackages() {
if (model == null) {
return Collections.emptyList();
} else {
return Collections.unmodifiableCollection(model);
}
}
@Override
public DefaultTypeStrategy getDefaultTypeStrategy() {
return new DefaultTypeStrategy() {
private Collection model = getModel();
public Object getDefaultAttributeType() {
return ModelUtils.findTypeInModel("Integer", model.iterator()
.next());
}
public Object getDefaultParameterType() {
return ModelUtils.findTypeInModel("Integer", model.iterator()
.next());
}
public Object getDefaultReturnType() {
return null;
}
};
}
}