// $Id: UmlFactoryEUMLImpl.java 122 2010-09-24 00:46:37Z marcusvnac $
// Copyright (c) 2007,2008 Tom Morris and other contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the ArgoUML Project nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE ArgoUML PROJECT ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE ArgoUML PROJECT BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package org.argouml.model.euml;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.argouml.model.NotImplementedException;
import org.argouml.model.AbstractModelFactory;
import org.argouml.model.IllegalModelElementConnectionException;
import org.argouml.model.MetaTypes;
import org.argouml.model.UmlFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.uml.Abstraction;
import org.eclipse.uml2.uml.AggregationKind;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.AssociationClass;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Extend;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Include;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.PackageImport;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.Transition;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.Usage;
import org.eclipse.uml2.uml.UseCase;
/**
* The implementation of the UmlFactory for EUML2.
*/
class UmlFactoryEUMLImpl implements UmlFactory, AbstractModelFactory {
/**
* The model implementation.
*/
private EUMLModelImplementation modelImpl;
/**
* The meta types factory.
*/
private MetaTypes metaTypes;
/**
* A map of valid connections keyed by the connection type. The constructor
* builds this from the data in the VALID_CONNECTIONS array
*/
private Map validConnectionMap = new HashMap();
/**
* An array of valid connections, the combination of connecting class and
* node classes must exist as a row in this list to be considered valid.
* <ul>
* <li>The 1st column is the connecting element.
* <li>The 2nd column is the "from" element type.
* <li>The 3rd column is the "to" element type.
* <li>The 3rd column is optional, if not given then it is assumed to be
* the same as the "to" element.
* <li>The existence of a 4th column indicates that the connection is valid
* in one direction only.
* </ul>
* TODO: This encodes not only what is legal in UML, but also what ArgoUML
* knows how to create, so not all legal connections are included. Probably
* should be split into two pieces: 1) legal UML (here) and 2) supported (in
* ArgoUML application someplace) - tfm - 20060325<p>
*
* Most of these are subtypes of Relationship which includes Association,
* Dependency, Flow, Generalization, Extend, and Include. Dependency
* includes Binding, Abstraction, Usage, and Permission. AssociationRole and
* AssociationClass are Associations. The remaining items (Link, Transition,
* AssociationEnd, Message) are non-Relationship types which ArgoUML treats
* as connections/edges.
*/
// TODO: This should be built by reflection from the metamodel - tfm
// Update for UML 2.x metamodel if not replaced by reflection
private static final Object[][] VALID_CONNECTIONS = {
{Generalization.class, Classifier.class, },
{Dependency.class, Element.class, },
// Although Usage & Permission are Dependencies, they need to
// be include separately because of the way lookup works
{Usage.class, NamedElement.class, },
{PackageImport.class, NamedElement.class, },
// The following is specifically for Realizations
// TODO: correction in GEF, it should use InterfaceRealization, not Abstraction
{Abstraction.class, org.eclipse.uml2.uml.Class.class, Interface.class, null, },
// The next 3 restrictions for Abstraction seem to be Argo specific
// not something the UML spec requires - tfm - 20070215
// There is no need for these because they arn't used by buildConnection() - b00__1
// {Abstraction.class, org.eclipse.uml2.uml.Class.class, org.eclipse.uml2.uml.Class.class, null, },
// {Abstraction.class, org.eclipse.uml2.uml.Package.class,org.eclipse.uml2.uml.Package.class, null, },
// {Abstraction.class, Component.class, Interface.class, null, },
{Association.class, Type.class, },
// {AssociationRole.class, ClassifierRole.class, },
{Extend.class, UseCase.class, },
{Include.class, UseCase.class, },
// {Link.class, Instance.class, },
// {Transition.class, StateVertex.class, },
{Transition.class, State.class, },
{AssociationClass.class, Type.class, },
// {AssociationEnd.class, Classifier.class, Association.class, },
// {Message.class, ClassifierRole.class },
};
/**
* Constructor.
*
* @param implementation
* The ModelImplementation.
*/
public UmlFactoryEUMLImpl(EUMLModelImplementation implementation) {
modelImpl = implementation;
metaTypes = modelImpl.getMetaTypes();
buildValidConnectionMap();
}
public Object buildConnection(Object elementType, Object fromElement,
Object fromStyle, Object toElement, Object toStyle,
Object unidirectional, Object namespace)
throws IllegalModelElementConnectionException {
IllegalModelElementConnectionException exception = new IllegalModelElementConnectionException(
"Cannot make a " + elementType.getClass().getName() //$NON-NLS-1$
+ " between a " + fromElement.getClass().getName() //$NON-NLS-1$
+ " and a " + toElement.getClass().getName()); //$NON-NLS-1$
if (!isConnectionValid(elementType, fromElement, toElement, true)) {
throw exception;
}
Object connection = null;
if (elementType == metaTypes.getAssociation()) {
connection = modelImpl.getCoreFactory().buildAssociation(
(Classifier) fromElement, (AggregationKind) fromStyle,
(Classifier) toElement, (AggregationKind) toStyle,
(Boolean) unidirectional);
} else if (elementType == metaTypes.getAssociationEnd()) {
if (fromElement instanceof Association) {
connection = modelImpl.getCoreFactory().buildAssociationEnd(
toElement, fromElement);
} else if (fromElement instanceof Classifier) {
connection = modelImpl.getCoreFactory().buildAssociationEnd(
fromElement, toElement);
}
} else if (elementType == metaTypes.getAssociationClass()) {
connection = modelImpl.getCoreFactory().buildAssociationClass(
fromElement, toElement);
} else if (elementType == metaTypes.getAssociationRole()) {
connection = modelImpl.getCollaborationsFactory().buildAssociationRole(
fromElement, fromStyle, toElement, toStyle,
(Boolean) unidirectional);
} else if (elementType == metaTypes.getGeneralization()) {
connection = modelImpl.getCoreFactory().buildGeneralization(
fromElement, toElement);
} else if (elementType == metaTypes.getPackageImport()) {
connection = modelImpl.getCoreFactory().buildPackageImport(
fromElement, toElement);
} else if (elementType == metaTypes.getUsage()) {
connection = modelImpl.getCoreFactory().buildUsage(
fromElement, toElement);
} else if (elementType == metaTypes.getDependency()) {
connection = modelImpl.getCoreFactory().buildDependency(
fromElement, toElement);
} else if (elementType == metaTypes.getAbstraction()) {
connection = modelImpl.getCoreFactory().buildRealization(
fromElement, toElement, namespace);
} else if (elementType == metaTypes.getLink()) {
connection = modelImpl.getCommonBehaviorFactory().buildLink(
fromElement, toElement);
} else if (elementType == metaTypes.getExtend()) {
// Extend, but only between two use cases. Remember we draw from the
// extension port to the base port.
connection = modelImpl.getUseCasesFactory().buildExtend(
toElement, fromElement);
} else if (elementType == metaTypes.getInclude()) {
connection = modelImpl.getUseCasesFactory().buildInclude(
fromElement, toElement);
} else if (elementType == metaTypes.getTransition()) {
connection = modelImpl.getStateMachinesFactory().buildTransition(
fromElement, toElement);
}
if (connection == null) {
throw exception;
}
return connection;
}
public Object buildNode(Object elementType, Object container) {
throw new NotImplementedException();
}
public Object buildNode(Object elementType) {
Object o = null;
if (elementType == metaTypes.getActor()) {
o = modelImpl.getUseCasesFactory().createActor();
} else if (elementType == metaTypes.getUseCase()) {
o = modelImpl.getUseCasesFactory().createUseCase();
} else if (elementType == metaTypes.getUMLClass()) {
o = modelImpl.getCoreFactory().buildClass();
} else if (elementType == metaTypes.getInterface()) {
o = modelImpl.getCoreFactory().buildInterface();
} else if (elementType == metaTypes.getDataType()) {
o = modelImpl.getCoreFactory().createDataType();
} else if (elementType == metaTypes.getPackage()) {
o = modelImpl.getModelManagementFactory().createPackage();
} else if (elementType == metaTypes.getModel()) {
o = modelImpl.getModelManagementFactory().createModel();
} else if (elementType == metaTypes.getInstance()) {
throw new IllegalArgumentException(
"Attempt to instantiate abstract type"); //$NON-NLS-1$
} else if (elementType == metaTypes.getSubsystem()) {
o = modelImpl.getModelManagementFactory().createSubsystem();
} else if (elementType == metaTypes.getCallState()) {
o = modelImpl.getActivityGraphsFactory().createCallState();
} else if (elementType == metaTypes.getSimpleState()) {
o = modelImpl.getStateMachinesFactory().createSimpleState();
} else if (elementType == metaTypes.getFinalState()) {
o = modelImpl.getStateMachinesFactory().createFinalState();
} else if (elementType == metaTypes.getPseudostate()) {
o = modelImpl.getStateMachinesFactory().createPseudostate();
} else if (elementType == metaTypes.getObjectFlowState()) {
o = modelImpl.getActivityGraphsFactory().createObjectFlowState();
} else if (elementType == metaTypes.getActionState()) {
o = modelImpl.getActivityGraphsFactory().createActionState();
} else if (elementType == metaTypes.getSubactivityState()) {
o = modelImpl.getActivityGraphsFactory().createSubactivityState();
} else if (elementType == metaTypes.getPartition()) {
o = modelImpl.getActivityGraphsFactory().createPartition();
} else if (elementType == metaTypes.getStubState()) {
o = modelImpl.getStateMachinesFactory().createStubState();
} else if (elementType == metaTypes.getSubmachineState()) {
o = modelImpl.getStateMachinesFactory().createSubmachineState();
} else if (elementType == metaTypes.getCompositeState()) {
o = modelImpl.getStateMachinesFactory().createCompositeState();
} else if (elementType == metaTypes.getSynchState()) {
o = modelImpl.getStateMachinesFactory().createSynchState();
} else if (elementType == metaTypes.getState()) {
throw new IllegalArgumentException(
"Attempt to instantiate abstract type"); //$NON-NLS-1$
} else if (elementType == modelImpl.getMetaTypes().getSimpleState()) {
o = modelImpl.getStateMachinesFactory().createSimpleState();
} else if (elementType == metaTypes.getClassifierRole()) {
o = modelImpl.getCollaborationsFactory().createClassifierRole();
} else if (elementType == metaTypes.getComponent()) {
o = modelImpl.getCoreFactory().createComponent();
} else if (elementType == metaTypes.getComponentInstance()) {
o = modelImpl.getCommonBehaviorFactory().createComponentInstance();
} else if (elementType == metaTypes.getNode()) {
o = modelImpl.getCoreFactory().createNode();
} else if (elementType == metaTypes.getNodeInstance()) {
o = modelImpl.getCommonBehaviorFactory().createNodeInstance();
} else if (elementType == metaTypes.getObject()) {
o = modelImpl.getCommonBehaviorFactory().createObject();
} else if (elementType == metaTypes.getComment()) {
o = modelImpl.getCoreFactory().createComment();
} else if (elementType == metaTypes.getNamespace()) {
throw new IllegalArgumentException(
"Attempt to instantiate abstract type"); //$NON-NLS-1$
} else if (elementType == metaTypes.getOperation()) {
o = modelImpl.getCoreFactory().createOperation();
} else if (elementType == metaTypes.getEnumeration()) {
o = modelImpl.getCoreFactory().createEnumeration();
} else if (elementType == metaTypes.getStereotype()) {
o = modelImpl.getExtensionMechanismsFactory().createStereotype();
} else if (elementType == metaTypes.getAttribute()) {
o = modelImpl.getCoreFactory().createAttribute();
} else if (elementType == metaTypes.getSignal()) {
o = modelImpl.getCommonBehaviorFactory().createSignal();
} else if (elementType == metaTypes.getException()) {
o = modelImpl.getCommonBehaviorFactory().createException();
} else if (elementType == metaTypes.getTransition()) {
o = modelImpl.getStateMachinesFactory().createTransition();
}
if (!(o instanceof EObject)) {
throw new IllegalArgumentException(
"Attempted to create unsupported model element type: " //$NON-NLS-1$
+ elementType);
}
return o;
}
public void delete(final Object elem) {
if (!(elem instanceof EObject)) {
throw new IllegalArgumentException(
"elem must be instance of EObject"); //$NON-NLS-1$
}
RunnableClass run = new RunnableClass() {
public void run() {
EcoreUtil.delete((EObject) elem);
}
};
modelImpl.getEditingDomain().getCommandStack().execute(
new ChangeCommand(
modelImpl, run, "Remove from the model the element #",
elem));
}
public boolean isRemoved(Object o) {
// This triggers some warnings (in logs) because some elements are
// created without an owner (and eResource is null)
// TODO: fix this
// The warning log (if we would not add the EObject to a resource) would
// looks like this: "...WARN [AWT-EventQueue-0] Encountered deleted
// object during delete of..."
if (o instanceof Element) {
return ((Element) o).eResource() == null;
}
throw new IllegalArgumentException("Not an Element : " + o); //$NON-NLS-1$
}
public boolean isConnectionType(Object connectionType) {
// TODO: Auto-generated method stub
throw new NotYetImplementedException();
}
public boolean isConnectionValid(Object connectionType, Object fromElement,
Object toElement, boolean checkWFR) {
// Get the list of valid model item pairs for the given connection type
List validItems = (ArrayList) validConnectionMap.get(connectionType);
if (validItems == null) {
return false;
}
// See if there's a pair in this list that match the given
// model elements
Iterator it = validItems.iterator();
while (it.hasNext()) {
Class[] modeElementPair = (Class[]) it.next();
if (modeElementPair[0].isInstance(fromElement)
&& modeElementPair[1].isInstance(toElement)) {
if (checkWFR) {
return isConnectionWellformed(
(Class) connectionType,
(Element) fromElement,
(Element) toElement);
} else {
return true;
}
}
}
return false;
}
public boolean isContainmentValid(Object metaType, Object container) {
throw new NotImplementedException();
}
/**
* Run through any well formedness rules we wish to enforce for a
* connection.
* @param connectionType
* @param fromElement
* @param toElement
* @return true if the connection satisfies the wellformedness rules
*/
private boolean isConnectionWellformed(
Class connectionType,
Element fromElement,
Element toElement) {
if (fromElement == null || toElement == null) {
return false;
}
if (connectionType == Generalization.class) {
/*
* UML 1.4.2 Spec section 4.5.3.20 [5]
* A GeneralizableElement may only be a child of
* GeneralizableElement of the same kind.
*/
if (fromElement.getClass() != toElement.getClass()) {
return false;
}
}
return true;
}
private void buildValidConnectionMap() {
// A list of valid connections between elements, the
// connection type first and then the elements to be connected
Object connection = null;
for (int i = 0; i < VALID_CONNECTIONS.length; ++i) {
connection = VALID_CONNECTIONS[i][0];
List validItems = (ArrayList) validConnectionMap.get(connection);
if (validItems == null) {
validItems = new ArrayList();
validConnectionMap.put(connection, validItems);
}
if (VALID_CONNECTIONS[i].length < 3) {
// If there isn't a 3rd column then this represents a connection
// of elements of the same type.
Object[] modeElementPair = new Class[2];
modeElementPair[0] = VALID_CONNECTIONS[i][1];
modeElementPair[1] = VALID_CONNECTIONS[i][1];
validItems.add(modeElementPair);
} else {
// If there is a 3rd column then this represents a connection
// of between 2 different types of element.
Object[] modeElementPair = new Class[2];
modeElementPair[0] = VALID_CONNECTIONS[i][1];
modeElementPair[1] = VALID_CONNECTIONS[i][2];
validItems.add(modeElementPair);
// If the array hasn't been flagged to indicate otherwise
// swap elements the elemnts and add again.
if (VALID_CONNECTIONS[i].length < 4
&& VALID_CONNECTIONS[i][1] != VALID_CONNECTIONS[i][2]) {
Object[] reversedModeElementPair = new Class[2];
reversedModeElementPair[0] = VALID_CONNECTIONS[i][2];
reversedModeElementPair[1] = VALID_CONNECTIONS[i][1];
validItems.add(reversedModeElementPair);
}
}
}
}
public void deleteExtent(Object element) {
// TODO: This is adequate because we only support a single editing
// domain right now, but it needs to be enhanced for multiple domains.
modelImpl.clearEditingDomain();
}
}