/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2007 Matthias Braeuer (braeuer.matthias@web.de). * * All rights reserved. * * * * This work was done as a project at the Chair for Software Technology, * * Dresden University Of Technology, Germany (http://st.inf.tu-dresden.de). * * It is understood that any modification not identified as such is not * * covered by the preceding statement. * * * * This work is free software; you can redistribute it and/or modify it * * under the terms of the GNU Library General Public License as published * * by the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This work is distributed in the hope that it will be useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public * * License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; if not, you can view it online at * * http://www.fsf.org/licensing/licenses/gpl.html. * * * * To submit a bug report, send a comment, or get the latest news on this * * project, please visit the website: http://dresden-ocl.sourceforge.net. * * For more information on OCL and related projects visit the OCL Portal: * * http://st.inf.tu-dresden.de/ocl * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * $Id$ */ package org.dresdenocl.essentialocl.expressions.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.apache.log4j.Logger; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.emf.ecore.util.EObjectContainmentEList; import org.eclipse.emf.ecore.util.InternalEList; import org.dresdenocl.essentialocl.expressions.OclExpression; import org.dresdenocl.essentialocl.expressions.OperationCallExp; import org.dresdenocl.essentialocl.expressions.WellformednessException; import org.dresdenocl.essentialocl.types.CollectionType; import org.dresdenocl.essentialocl.types.TupleType; import org.dresdenocl.essentialocl.types.TypeType; import org.dresdenocl.pivotmodel.ComplexGenericType; import org.dresdenocl.pivotmodel.Feature; import org.dresdenocl.pivotmodel.Operation; import org.dresdenocl.pivotmodel.Parameter; import org.dresdenocl.pivotmodel.PrimitiveType; import org.dresdenocl.pivotmodel.PrimitiveTypeKind; import org.dresdenocl.pivotmodel.Property; import org.dresdenocl.pivotmodel.Type; import org.dresdenocl.pivotmodel.TypeParameter; /** * <!-- begin-user-doc --> An implementation of the model object ' * <em><b>Operation Call Exp</b></em>'. <!-- end-user-doc --> * <p> * The following features are implemented: * <ul> * <li>{@link org.dresdenocl.essentialocl.expressions.impl.OperationCallExpImpl#getArgument <em>Argument</em>}</li> * <li>{@link org.dresdenocl.essentialocl.expressions.impl.OperationCallExpImpl#getReferredOperation <em>Referred Operation</em>}</li> * </ul> * </p> * * @generated */ public class OperationCallExpImpl extends FeatureCallExpImpl implements OperationCallExp { /** * Logger for this class */ private static final Logger logger = Logger .getLogger(OperationCallExpImpl.class); /** * The cached value of the '{@link #getArgument() <em>Argument</em>}' containment reference list. * <!-- begin-user-doc --> <!-- end-user-doc --> * @see #getArgument() * @generated * @ordered */ protected EList<OclExpression> argument; /** * The cached value of the '{@link #getReferredOperation() <em>Referred Operation</em>}' reference. * <!-- begin-user-doc --> <!-- * end-user-doc --> * @see #getReferredOperation() * @generated * @ordered */ protected Operation referredOperation; /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ protected OperationCallExpImpl() { super(); } /** * Overridden to determine the type of this <code>OperationCallExp</code>. * Unfortunately, the OCL Specification does not define any wellformedness * rules for the type of an <code>OperationCallExp</code>. As a result, this * implementation simply follows the approach taken by the last release of * the Dresden OCL2 Toolkit. * * <p> * There, the type of an Operation Call Expression is the result type of the * referred operation or a tuple type comprising the result type of the * operation and all out and inout parameters. * </p> * * @see org.dresdenocl.pivotmodel.impl.TypedElementImpl#getType() */ @Override protected Type evaluateType() { if (logger.isDebugEnabled()) { logger.debug("evaluateType() - enter"); //$NON-NLS-1$ } Type type; /* * If the referredOperation is null and the name of the OperationCallExp * instance is 'atPre' then the method 'withAtPre()' was used before. So * in this case we delegate the call to the source expression. */ if (referredOperation == null) { if (name.equals("atPre")) { return source.getType(); } } // check wellformedness of abstract syntax if (referredOperation == null) { throw new WellformednessException(this, "The referred operation of an OperationCallExp must not be null."); //$NON-NLS-1$ } // if there are any output parameters, the expression type is a tuple // type else if (referredOperation.getOutputParameter().size() > 0) { List<Property> tupleTypeProperties = new ArrayList<Property>(); // add all output parameters for (Parameter parameter : referredOperation.getOutputParameter()) { tupleTypeProperties.add(parameter.asProperty()); } // add the return parameter if existing if (referredOperation.getReturnParameter() != null) { tupleTypeProperties.add(referredOperation.getReturnParameter() .asProperty()); } type = getValidOclLibrary().makeTupleType(tupleTypeProperties); } // otherwise default to the operation's type else { // TODO: remove explicit references to the OCL Standard Library from // code String operationName = referredOperation.getName(); // bind 'allInstances' operation if (operationName.equals("allInstances")) { //$NON-NLS-1$ referredOperation = bindAllInstancesOperation(referredOperation); } // bind 'asSet' operation else if (!(source.getType() instanceof CollectionType) && operationName.equals("asSet")) { //$NON-NLS-1$ referredOperation = bindAsSetOperation(referredOperation); } // bind 'oclAsType' operation else if (operationName.equals("oclAsType")) { //$NON-NLS-1$ referredOperation = bindOclAsTypeOperation(referredOperation); } // bind 'oclType' operation else if (operationName.equals("oclType")) { //$NON-NLS-1$ referredOperation = bindOclTypeOperation(referredOperation); } // bind 'flatten' operation else if (getSourceType() instanceof CollectionType) { if (operationName.equals("flatten")) { //$NON-NLS-1$ referredOperation = bindFlattenOperation(referredOperation); } else if (operationName.equals("product")) { //$NON-NLS-1$ referredOperation = bindProductOperation(referredOperation); } } /* Bind 'characters' operation. */ else if (getSourceType() instanceof PrimitiveType && ((PrimitiveType) getSourceType()).getKind() == PrimitiveTypeKind.STRING) { if (operationName.equals("characters")) { //$NON-NLS-1$ referredOperation = bindCharactersOperation(referredOperation); } // no else. } // no else. /* Map the operation's type to a corresponding OCL type. */ type = getOclType(referredOperation.getType()); } if (logger.isDebugEnabled()) { logger.debug("evaluateType() - exit - return value=" + type); //$NON-NLS-1$ } return type; } /** * FIXME Claas: Isn't there a general solution to solve the binding of * generic operations? * * <p> * Helper method to bind the <code>String.characters()</code> * {@link Operation} . * </p> * * @param The * <code>String.characters()</code> {@link Operation} that shall * be bound to this {@link OperationCallExp}. */ private Operation bindCharactersOperation(Operation charactersOperation) { /* Probably log entry. */ if (logger.isDebugEnabled()) { logger.debug("bindCharactersOperation(charactersOperation=" + charactersOperation //$NON-NLS-1$ + ") - enter"); //$NON-NLS-1$ } // no else. PrimitiveType sourceType; /* It is safe to cast here since we checked in evaluateType. */ sourceType = (PrimitiveType) getSourceType(); /* Bind the operation. */ charactersOperation = charactersOperation.bindTypeParameter( new ArrayList<TypeParameter>( ((ComplexGenericType) charactersOperation .getGenericType()).getUnboundType() .getOwnedTypeParameter()), Arrays .asList(sourceType)); /* Probably log exit. */ if (logger.isDebugEnabled()) { logger.debug("bindCharactersOperation() - exit - return value=" //$NON-NLS-1$ + charactersOperation); } // no else. return charactersOperation; } /** * Helper method to bind the 'flatten' operation. The definition is the same * for Set, Bag, and Sequence. E.g.: * * <p> * If the element type is not a collection type, this results in the same * self. If the element type is a collection type, the result is the set * containing all the elements of all the elements of self. * * <pre> * post: result = if self.type.elementType.oclIsKindOf(CollectionType) then * self->iterate(c; acc : Set() = Set{} | * acc->union(c->asSet() ) ) * else * self * endif * </pre> * * </p> */ private Operation bindFlattenOperation(Operation flattenOperation) { if (logger.isDebugEnabled()) { logger.debug("bindFlattenOperation(flattenOperation=" + flattenOperation //$NON-NLS-1$ + ") - enter"); //$NON-NLS-1$ } CollectionType sourceType; Type elementType; // it is safe to cast here since we checked in evaluateType sourceType = (CollectionType) getSourceType(); elementType = sourceType.getElementType(); elementType = checkForNestedCollection(elementType); // bind the operation flattenOperation = flattenOperation.bindTypeParameter( new ArrayList<TypeParameter>(flattenOperation .getOwnedTypeParameter()), Arrays.asList(elementType)); if (logger.isDebugEnabled()) { logger.debug("bindFlattenOperation() - exit - return value=" //$NON-NLS-1$ + flattenOperation); } return flattenOperation; } private Type checkForNestedCollection(Type elementType) { // check the element type of the source collection type if (elementType instanceof CollectionType) { elementType = ((CollectionType) elementType).getElementType(); elementType = checkForNestedCollection(elementType); } return elementType; } /** * FIXME Claas: Isn't there a general solution to solve the binding of * generic operations? * * <p> * Helper method to bind the <code>OclAny.oclType()</code> {@link Operation} * . * </p> * * @param The * <code>OclAny.asSet()</code> {@link Operation} that shall be * bound to this {@link OperationCallExp}. */ private Operation bindAsSetOperation(Operation asSetOperation) { /* Probably log the entry of this method. */ if (logger.isDebugEnabled()) { logger.debug("bindAsSetOperation(oclTypeOperation=" //$NON-NLS-1$ + asSetOperation + ") - enter"); //$NON-NLS-1$ } // no else. /* Get the source's type. */ Type elementType; elementType = this.getSource().getType(); /* Check that the type is not null. */ if (elementType == null) { throw new WellformednessException(this, "The source of the 'asSet' operation must be defined."); //$NON-NLS-1$ } // no else. /* Check argument size. */ if (getArgument().size() != 0) { throw new WellformednessException(this, "The 'asSet' operation has not to have any argument."); //$NON-NLS-1$ } // no else. /* Bind the asSet operation, which will set its return type. */ asSetOperation = asSetOperation.bindTypeParameter( new ArrayList<TypeParameter>(asSetOperation .getOwnedTypeParameter()), Arrays.asList(elementType)); /* Probably log the exit of this method. */ if (logger.isDebugEnabled()) { logger.debug("bindAsSetOperation() - exit - return value=" //$NON-NLS-1$ + asSetOperation); } // no else. return asSetOperation; } /** * Helper method to bind the 'OclAny::oclAsType' operation * * TODO: Clean up this implementation with something smarter. The current * code is a pretty big hack, because we need to know the signature of the * 'oclAsType' operation (i.e., exactly one parameter of type OclType). It * would be more clever to implement something like the automatic binding of * generic method type parameters in Java. More precisely, we first look for * an occurence of the method's type parameter in the (unbound) signature * and then find out what type has been bound at this position. */ private Operation bindOclAsTypeOperation(Operation oclAsTypeOperation) { if (logger.isDebugEnabled()) { logger.debug("bindOclAsTypeOperation(oclAsTypeOperation=" //$NON-NLS-1$ + oclAsTypeOperation + ") - enter"); //$NON-NLS-1$ } OclExpression argument; TypeType argumentType; Type representedType; // check arguments if (getArgument().size() != 1) { throw new WellformednessException(this, "The 'oclAsType' operation must have exactly one argument."); //$NON-NLS-1$ } // get the single argument of the 'oclAsType' operation argument = getArgument().get(0); // check type of argument if (!(argument.getType() instanceof TypeType)) { throw new WellformednessException(this, "The operation 'oclAsType' must have an OclType as its argument."); //$NON-NLS-1$ } argumentType = (TypeType) argument.getType(); // get the type that is represented by the TypeType representedType = argumentType.getRepresentedType(); if (representedType == null) { throw new WellformednessException(this, "Unable to determine the type represented by the passed OclType."); //$NON-NLS-1$ } // bind the oclAsType operation, which will set its return type oclAsTypeOperation = oclAsTypeOperation.bindTypeParameter( new ArrayList<TypeParameter>(oclAsTypeOperation .getOwnedTypeParameter()), Arrays .asList(representedType)); if (logger.isDebugEnabled()) { logger.debug("bindOclAsTypeOperation() - exit - return value=" //$NON-NLS-1$ + oclAsTypeOperation); } return oclAsTypeOperation; } /** * FIXME Claas: Isn't there a general solution to solve the binding of * generic operations? * * <p> * Helper method to bind the <code>OclAny.oclType()</code> {@link Operation} * . * </p> * * @param The * <code>OclAny.oclType()</code> {@link Operation} that shall be * bound to this {@link OperationCallExp}. */ private Operation bindOclTypeOperation(Operation oclTypeOperation) { /* Probably log the entry of this method. */ if (logger.isDebugEnabled()) { logger.debug("bindOclTypeOperation(oclTypeOperation=" //$NON-NLS-1$ + oclTypeOperation + ") - enter"); //$NON-NLS-1$ } // no else. /* Get the source's type. */ Type representedType; representedType = this.getSource().getType(); /* Check that the type is not null. */ if (representedType == null) { throw new WellformednessException(this, "The source of the 'oclType' operation must be defined."); //$NON-NLS-1$ } // no else. /* Check argument size. */ if (getArgument().size() != 0) { throw new WellformednessException(this, "The 'oclType' operation has not to have any argument."); //$NON-NLS-1$ } // no else. /* Bind the oclType operation, which will set its return type. */ oclTypeOperation = oclTypeOperation.bindTypeParameter( new ArrayList<TypeParameter>(oclTypeOperation .getOwnedTypeParameter()), Arrays .asList(representedType)); /* Probably log the exit of this method. */ if (logger.isDebugEnabled()) { logger.debug("bindOclTypeOperation() - exit - return value=" //$NON-NLS-1$ + oclTypeOperation); } // no else. return oclTypeOperation; } /** * FIXME Claas: Isn't there a general solution to solve the binding of * generic operations? * * <p> * Helper method to bind the * <code>OclCollection<T1>.product(Collection<T2>)</code> {@link Operation} * . * </p> * * @param The * <code>OclCollection<T1>.product(Collection<T2>)</code> * {@link Operation} that shall be bound to this * {@link OperationCallExp}. */ private Operation bindProductOperation(Operation productOperation) { /* Probably log the entry of this method. */ if (logger.isDebugEnabled()) { logger.debug("bindProductOperation(productOperation=" //$NON-NLS-1$ + productOperation + ") - enter"); //$NON-NLS-1$ } // no else. /* Check argument size. */ if (getArgument().size() != 1) { throw new WellformednessException(this, "The 'product' operation must have exactly one argument that is a collection."); //$NON-NLS-1$ } // no else. /* Get the param's type. */ Type paramType; paramType = this.getArgument().get(0).getType(); /* Check that the type is not null. */ if (paramType == null) { throw new WellformednessException(this, "The params's type of the 'product' operation must be defined."); //$NON-NLS-1$ } // no else. if (!(this.getSource().getType() instanceof CollectionType)) { throw new WellformednessException(this, "The source's type of the 'product' operation must be a collection type."); //$NON-NLS-1$ } // no else. Type paramElementType; paramElementType = ((CollectionType) paramType).getElementType(); /* Bind the product operation, which will set its return type. */ productOperation = productOperation.bindTypeParameter( new ArrayList<TypeParameter>(productOperation .getOwnedTypeParameter()), Arrays .asList(paramElementType)); /* Probably log the exit of this method. */ if (logger.isDebugEnabled()) { logger.debug("bindProductOperation() - exit - return value=" //$NON-NLS-1$ + productOperation); } // no else. return productOperation; } /** * Helper method to bind the 'OclAny::allInstances' operation */ private Operation bindAllInstancesOperation(Operation allInstancesOperation) { if (logger.isDebugEnabled()) { logger.debug("bindAllInstancesOperation(allInstancesOperation=" //$NON-NLS-1$ + allInstancesOperation + ") - enter"); //$NON-NLS-1$ } /* Determine the source type. */ Type srcType = ((TypeType) getSourceType()).getRepresentedType(); /* * allInstances may only refer to types with a finite number of * instances. */ if ((srcType instanceof PrimitiveType && ((PrimitiveType) srcType) .getKind() != PrimitiveTypeKind.BOOLEAN) || srcType instanceof CollectionType || srcType instanceof TupleType) { throw new WellformednessException(this, "The 'allInstances' operation cannot be invoked on '" //$NON-NLS-1$ + srcType.getName() + "'."); //$NON-NLS-1$ } // now bind the 'allInstances' operation with the source type allInstancesOperation = allInstancesOperation.bindTypeParameter( new ArrayList<TypeParameter>(allInstancesOperation .getOwnedTypeParameter()), Arrays.asList(srcType)); if (logger.isDebugEnabled()) { logger.debug("bindAllInstancesOperation() - exit - return value=" //$NON-NLS-1$ + allInstancesOperation); } return allInstancesOperation; } /* * (non-Javadoc) * * @see * org.dresdenocl.essentialocl.expressions.impl.FeatureCallExpImpl * #getFeature() */ @Override protected Feature getFeature() { return getReferredOperation(); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ public List<OclExpression> getArgument() { if (argument == null) { argument = new EObjectContainmentEList<OclExpression>( OclExpression.class, this, ExpressionsPackageImpl.OPERATION_CALL_EXP__ARGUMENT); } return argument; } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ public Operation getReferredOperation() { return referredOperation; } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ public void setReferredOperation(Operation newReferredOperation) { Operation oldReferredOperation = referredOperation; referredOperation = newReferredOperation; if (eNotificationRequired()) eNotify(new ENotificationImpl( this, Notification.SET, ExpressionsPackageImpl.OPERATION_CALL_EXP__REFERRED_OPERATION, oldReferredOperation, referredOperation)); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ @Override public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) { switch (featureID) { case ExpressionsPackageImpl.OPERATION_CALL_EXP__ARGUMENT: return ((InternalEList<?>) getArgument()).basicRemove(otherEnd, msgs); } return super.eInverseRemove(otherEnd, featureID, msgs); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ @Override public Object eGet(int featureID, boolean resolve, boolean coreType) { switch (featureID) { case ExpressionsPackageImpl.OPERATION_CALL_EXP__ARGUMENT: return getArgument(); case ExpressionsPackageImpl.OPERATION_CALL_EXP__REFERRED_OPERATION: return getReferredOperation(); } return super.eGet(featureID, resolve, coreType); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ @SuppressWarnings("unchecked") @Override public void eSet(int featureID, Object newValue) { switch (featureID) { case ExpressionsPackageImpl.OPERATION_CALL_EXP__ARGUMENT: getArgument().clear(); getArgument() .addAll((Collection<? extends OclExpression>) newValue); return; case ExpressionsPackageImpl.OPERATION_CALL_EXP__REFERRED_OPERATION: setReferredOperation((Operation) newValue); return; } super.eSet(featureID, newValue); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ @Override public void eUnset(int featureID) { switch (featureID) { case ExpressionsPackageImpl.OPERATION_CALL_EXP__ARGUMENT: getArgument().clear(); return; case ExpressionsPackageImpl.OPERATION_CALL_EXP__REFERRED_OPERATION: setReferredOperation((Operation) null); return; } super.eUnset(featureID); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ @Override public boolean eIsSet(int featureID) { switch (featureID) { case ExpressionsPackageImpl.OPERATION_CALL_EXP__ARGUMENT: return argument != null && !argument.isEmpty(); case ExpressionsPackageImpl.OPERATION_CALL_EXP__REFERRED_OPERATION: return referredOperation != null; } return super.eIsSet(featureID); } /** * <!-- begin-user-doc --> <!-- end-user-doc --> * @generated */ @Override protected EClass eStaticClass() { return ExpressionsPackageImpl.Literals.OPERATION_CALL_EXP; } /** * Overridden for unified toString appearance * * @see java.lang.Object#toString() */ @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .appendSuper(super.toString()) .append("referredOperation", referredOperation).toString(); //$NON-NLS-1$ } } // OperationCallExpImpl