/***************************************************************************** * Copyright (c) 2011 CEA LIST. * * 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: * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation *****************************************************************************/ package org.eclipse.papyrus.uml.properties.creation; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.papyrus.infra.emf.utils.EMFHelper; import org.eclipse.papyrus.views.properties.creation.EcorePropertyEditorFactory; import org.eclipse.swt.widgets.Control; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.InstanceValue; import org.eclipse.uml2.uml.Message; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Parameter; import org.eclipse.uml2.uml.ParameterDirectionKind; import org.eclipse.uml2.uml.PrimitiveType; import org.eclipse.uml2.uml.Type; import org.eclipse.uml2.uml.UMLPackage; /** * A factory to instantiate arguments corresponding to Message signatures * The arguments are pre-filled with the right name and type, which * are extracted from the corresponding parameter * * @author Camille Letavernier */ public class MessageValueSpecificationFactory extends EcorePropertyEditorFactory { /** * The message in which the arguments will be created */ protected Message parent; /** * Indicates the liberty we let to the user. * If set to true, he won't be able to instantiate invalid elements, * ie. he cannot instantiate arguments which don't correspond to an * operation's parameter. */ protected boolean restrictedInstantiation = false; /** * The directions of the parameters we want to retain */ protected Set<ParameterDirectionKind> directions; /** * * Constructor. * * @param type * The type that will be instantiated * @param parent * The parent Message * @param directions * The directions of the parameters we want to retain */ public MessageValueSpecificationFactory(EReference reference, Message parent, Set<ParameterDirectionKind> directions) { super(reference); this.parent = parent; this.directions = directions; } /** * {@inheritDoc} */ @Override protected List<EClass> getAvailableEClasses() { List<EClass> allClasses = EMFHelper.getSubclassesOf(type, true); List<EClass> result = new LinkedList<EClass>(); for(EClass eClass : allClasses) { if(isValid(eClass)) { result.add(eClass); } } return result; } /** * {@inheritDoc} */ @Override public Object createObject(Control widget) { EClass eClass = chooseEClass(widget); if(eClass == null) { return null; } EObject instance = eClass.getEPackage().getEFactoryInstance().create(eClass); if(instance != null && instance instanceof NamedElement) { Parameter parameter = getParameter(); if(parameter != null) { ((NamedElement)instance).setName(parameter.getName()); } if(instance instanceof InstanceValue) { ((InstanceValue)instance).setType(parameter.getType()); } } return super.createObject(widget, instance); } /** * Tests if the given EClass can be instantiated for the following * parameter * * @param eClass * The EClass to test * @return * True if the EClass is a valid type for the next parameter * * @see #getParameter() */ protected boolean isValid(EClass eClass) { Parameter parameter = getParameter(); if(parameter == null) { return !restrictedInstantiation; } Type parameterType = parameter.getType(); if(parameterType instanceof PrimitiveType) { return isValidType(eClass, (PrimitiveType)parameterType); } if(parameterType instanceof Classifier) { return eClass == UMLPackage.eINSTANCE.getInstanceValue(); } return !restrictedInstantiation; //The operation has no signature } /** * * @return the Operation corresponding to the message's signature, * or null if the message's signature is not an operation */ protected Operation getOperation() { NamedElement namedElement = parent.getSignature(); if(namedElement instanceof Operation) { return (Operation)namedElement; } return null; } /** * * @return the next parameter from the operation. The next parameter * is the first operation's parameter that isn't matched by an argument * of the parent message. * * @see #getOperation() */ protected Parameter getParameter() { Operation operation = getOperation(); if(operation == null) { return null; } int index = parent.getArguments().size(); int i = 0; for(Parameter parameter : operation.getOwnedParameters()) { ParameterDirectionKind direction = parameter.getDirection(); if(directions.contains(direction)) { if(i++ == index) { return parameter; } } } return null; } /** * Tests if the given EClass is a valid type for the given PrimitiveType * This test is pretty subjective, as it tries to associate a custom primitive * type to a UML Literal type (or InstanceValue). * * For example, the UML "Literal Integer" can match the "Integer" or "int" * primitive type, which means that an instance of the "Integer" Primitive * Type is a valid value for a Literal Integer. * * @param eClass * A Subclass of InstanceSpecification * @param parameterType * A PrimitiveType * @return * True if an instance of the given PrimitiveType is a valid instance for the given eClass */ //TODO : To make this method a little more usable with custom primitive //types, and a little less subjective, the matching should be done through //an extension point or a local customization (preferences). //This currently works only with basic UML Primitive Types and standard //java-like types protected boolean isValidType(EClass eClass, PrimitiveType parameterType) { String typeName = parameterType.getName(); //Integer numbers if(eClass == UMLPackage.eINSTANCE.getLiteralInteger() || eClass == UMLPackage.eINSTANCE.getLiteralUnlimitedNatural()) { return typeName.equals("Integer") || typeName.equals("int"); //$NON-NLS-1$ //$NON-NLS-2$ } //Can be used to instantiate anything, except integers and booleans if(eClass == UMLPackage.eINSTANCE.getLiteralString()) { return !(typeName.equals("Integer") || typeName.equals("int") || typeName.equals("Boolean") || typeName.equals("boolean")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } //Can be used to instantiate anything, except integers, booleans and strings if(eClass == UMLPackage.eINSTANCE.getInstanceValue()) { return !(typeName.equals("Integer") || typeName.equals("int") || typeName.equals("Boolean") || typeName.equals("boolean") || typeName.equals("String")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } //Booleans if(eClass == UMLPackage.eINSTANCE.getLiteralBoolean()) { return typeName.equals("Boolean") || typeName.equals("boolean"); //$NON-NLS-1$ //$NON-NLS-2$ } //We aren't interested in other InstanceSpecifications return false; } }