OperationFacadeLogicImpl.java
package org.andromda.metafacades.emf.uml22;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import org.andromda.metafacades.uml.ClassifierFacade;
import org.andromda.metafacades.uml.ConstraintFacade;
import org.andromda.metafacades.uml.DependencyFacade;
import org.andromda.metafacades.uml.MetafacadeUtils;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.NameMasker;
import org.andromda.metafacades.uml.OperationFacade;
import org.andromda.metafacades.uml.ParameterFacade;
import org.andromda.metafacades.uml.TypeMappings;
import org.andromda.metafacades.uml.UMLMetafacadeProperties;
import org.andromda.metafacades.uml.UMLProfile;
import org.andromda.translation.ocl.ExpressionKinds;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.uml2.uml.CallConcurrencyKind;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.ParameterDirectionKind;
import org.eclipse.uml2.uml.Type;
/**
* MetafacadeLogic implementation for
* org.andromda.metafacades.uml.OperationFacade.
*
* @see org.andromda.metafacades.uml.OperationFacade
* @author Bob Fields
*/
public class OperationFacadeLogicImpl
extends OperationFacadeLogic
{
private static final long serialVersionUID = 34L;
/**
* The logger instance.
*/
private static final Logger LOGGER = Logger.getLogger(OperationFacadeLogicImpl.class);
/**
* @param metaObject
* @param context
*/
public OperationFacadeLogicImpl(
final Operation metaObject,
final String context)
{
super(metaObject, context);
}
/**
* Not yet implemented, always returns null. To implement: walk through the
* related elements from the Sequence Diagram or OCL Body in the UML model to produce compilable code.
* @return null
* @see org.andromda.metafacades.uml.OperationFacade#getMethodBody()
*/
@Override
protected String handleGetMethodBody()
{
return null;
}
/**
* Overridden to provide name masking.
*
* @see org.andromda.metafacades.uml.ModelElementFacade#getName()
*/
@Override
protected String handleGetName()
{
final String nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.OPERATION_NAME_MASK));
return NameMasker.mask(
super.handleGetName(),
nameMask);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getSignature()
*/
@Override
protected String handleGetSignature()
{
return this.getSignature(true);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getCall()
*/
@Override
protected String handleGetCall()
{
return this.getCall(this.handleGetName());
}
/**
* Constructs the operation call with the given <code>name</code>
*
* @param name
* the name form which to construct the operation call.
* @return the operation call.
*/
private String getCall(final String name)
{
final StringBuilder buffer = new StringBuilder(name);
buffer.append('(');
buffer.append(this.getArgumentNames());
buffer.append(')');
return buffer.toString();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getTypedArgumentList()
*/
@Override
protected String handleGetTypedArgumentList()
{
return this.getTypedArgumentList(true);
}
private String getTypedArgumentList(final boolean withArgumentNames)
{
// TODO: Possible covariant of the method 'getTypedArgumentList' defined in the class 'OperationFacadeLogic'
return MetafacadeUtils.getTypedArgumentList(
this.getArguments(),
withArgumentNames,
null);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isStatic()
*/
@Override
protected boolean handleIsStatic()
{
return this.metaObject.isStatic();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isAbstract()
*/
@Override
protected boolean handleIsAbstract()
{
return this.metaObject.isAbstract();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getExceptionList()
*/
@Override
protected String handleGetExceptionList()
{
return this.getExceptionList(null);
}
/**
* Finds both exceptions and exception dependency references
* @see org.andromda.metafacades.uml.OperationFacade#getExceptions()
*/
@Override
protected Collection handleGetExceptions()
{
final Collection<DependencyFacade> exceptions = new LinkedHashSet<DependencyFacade>();
// finds both exceptions and exception references
final class ExceptionFilter
implements Predicate
{
public boolean evaluate(final Object object)
{
boolean hasException = object instanceof DependencyFacade;
if (hasException)
{
final DependencyFacade dependency = (DependencyFacade)object;
// first check for exception references
hasException = dependency.hasStereotype(UMLProfile.STEREOTYPE_EXCEPTION_REF);
// if there wasn't any exception reference
// now check for actual exceptions
if (!hasException)
{
final ModelElementFacade targetElement = dependency.getTargetElement();
hasException = targetElement != null && targetElement.hasStereotype(
UMLProfile.STEREOTYPE_EXCEPTION);
}
}
return hasException;
}
}
// first get any dependencies on this operation's
// owner (because these will represent the default exception(s))
final Collection<DependencyFacade> ownerDependencies = new ArrayList<DependencyFacade>(this.getOwner().getSourceDependencies());
if (!ownerDependencies.isEmpty())
{
CollectionUtils.filter(ownerDependencies, new ExceptionFilter());
exceptions.addAll(ownerDependencies);
}
final Collection<DependencyFacade> operationDependencies = new ArrayList<DependencyFacade>(this.getSourceDependencies());
// now get any exceptions directly on the operation
if (!operationDependencies.isEmpty())
{
CollectionUtils.filter(operationDependencies, new ExceptionFilter());
exceptions.addAll(operationDependencies);
}
// now transform the dependency(s) to the actual exception(s)
CollectionUtils.transform(exceptions, new Transformer()
{
public Object transform(final Object object)
{
if (object == null) return null;
return ((DependencyFacade)object).getTargetElement();
}
});
// finally add in any members of the UML2 RaisedException list
// (the 'proper' UML2 way of doing exceptions .. or at least one way).
final Collection<Type> raisedExceptions = this.metaObject.getRaisedExceptions();
exceptions.addAll(this.shieldedElements(raisedExceptions));
return exceptions;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isReturnTypePresent()
*/
@Override
protected boolean handleIsReturnTypePresent()
{
boolean hasReturnType = false;
if (this.getReturnType() != null)
{
hasReturnType = !("void".equalsIgnoreCase(StringUtils.trimToEmpty(this.getReturnType().getFullyQualifiedName()))
|| StringUtils.trimToEmpty(this.getReturnType().getFullyQualifiedName(true)).equals(UMLProfile.VOID_TYPE_NAME));
}
if (LOGGER.isDebugEnabled())
{
final String rtnFQN = this.getReturnType().getFullyQualifiedName(true);
final boolean voidType = "void".equalsIgnoreCase(StringUtils.trimToEmpty(this.getReturnType().getFullyQualifiedName()));
final String voidRtn = this.getReturnType().getFullyQualifiedName();
LOGGER.debug("OperationFacadeLogicImpl.handleIsReturnTypePresent rtnFQN=" + rtnFQN + " voidType=" + voidType + " voidRtn=" + voidRtn + " hasReturnType=" + hasReturnType);
}
return hasReturnType;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isExceptionsPresent()
*/
@Override
protected boolean handleIsExceptionsPresent()
{
return !this.getExceptions().isEmpty();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getArgumentNames()
*/
@Override
protected String handleGetArgumentNames()
{
final StringBuilder buffer = new StringBuilder();
final Iterator<Parameter> iterator = this.metaObject.getOwnedParameters().iterator();
boolean commaNeeded = false;
final String comma = ", ";
while (iterator.hasNext())
{
final Parameter parameter = iterator.next();
if (!parameter.getDirection().equals(ParameterDirectionKind.RETURN_LITERAL))
{
if (commaNeeded)
{
buffer.append(comma);
}
final ParameterFacade facade = (ParameterFacade)this.shieldedElement(parameter);
buffer.append(facade.getName());
commaNeeded = true;
}
}
return buffer.toString();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getArgumentTypeNames()
*/
@Override
protected String handleGetArgumentTypeNames()
{
final StringBuilder buffer = new StringBuilder();
final Iterator<Parameter> iterator = this.metaObject.getOwnedParameters().iterator();
boolean commaNeeded = false;
while (iterator.hasNext())
{
final Parameter parameter = iterator.next();
if (!parameter.getDirection().equals(ParameterDirectionKind.RETURN_LITERAL))
{
if (commaNeeded)
{
buffer.append(", ");
}
final ParameterFacade facade = (ParameterFacade)this.shieldedElement(parameter);
buffer.append(facade.getType().getFullyQualifiedName());
commaNeeded = true;
}
}
return buffer.toString();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isQuery()
*/
@Override
protected boolean handleIsQuery()
{
return this.metaObject.isQuery();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getConcurrency()
*/
@Override
protected String handleGetConcurrency()
{
String concurrency = null;
final CallConcurrencyKind concurrencyKind = this.metaObject.getConcurrency();
if (concurrencyKind == null || concurrencyKind.equals(CallConcurrencyKind.CONCURRENT_LITERAL))
{
concurrency = "concurrent";
}
else if (concurrencyKind.equals(CallConcurrencyKind.GUARDED_LITERAL))
{
concurrency = "guarded";
}
else// CallConcurrencyKindEnum.CCK_SEQUENTIAL
{
concurrency = "sequential";
}
final TypeMappings languageMappings = this.getLanguageMappings();
if (languageMappings != null)
{
concurrency = languageMappings.getTo(concurrency);
}
return concurrency;
}
/**
* Gets the pattern for constructing the precondition name.
*
* @return the precondition pattern.
*/
private String getPreconditionPattern()
{
return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.PRECONDITION_NAME_PATTERN));
}
/**
* Gets the pattern for constructing the postcondition name.
*
* @return the postcondition pattern.
*/
private String getPostconditionPattern()
{
return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.POSTCONDITION_NAME_PATTERN));
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getPreconditionName()
*/
@Override
protected String handleGetPreconditionName()
{
return this.getPreconditionPattern().replaceAll(
"\\{0\\}",
this.handleGetName());
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getPostconditionName()
*/
@Override
protected String handleGetPostconditionName()
{
return this.getPostconditionPattern().replaceAll(
"\\{0\\}",
this.handleGetName());
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getPreconditionSignature()
*/
@Override
protected String handleGetPreconditionSignature()
{
return MetafacadeUtils.getSignature(
this.getPreconditionName(),
this.getArguments(),
true,
null);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getPreconditionCall()
*/
@Override
protected String handleGetPreconditionCall()
{
return this.getCall(this.getPreconditionName());
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isPreconditionsPresent()
*/
@Override
protected boolean handleIsPreconditionsPresent()
{
final Collection<ConstraintFacade> preconditions = this.getPreconditions();
return preconditions != null && !preconditions.isEmpty();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#isPostconditionsPresent()
*/
@Override
protected boolean handleIsPostconditionsPresent()
{
final Collection<ConstraintFacade> postconditions = this.getPostconditions();
return postconditions != null && !postconditions.isEmpty();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#findTaggedValue(String,
* boolean)
*/
@Override
protected Object handleFindTaggedValue(
final String name,
final boolean follow)
{
final String trimName = StringUtils.trimToEmpty(name);
Object value = this.findTaggedValue(trimName);
if (follow)
{
ClassifierFacade type = this.getReturnType();
while (value == null && type != null)
{
value = type.findTaggedValue(trimName);
type = (ClassifierFacade)type.getGeneralization();
}
}
return value;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getExceptionList(String)
*/
@Override
protected String handleGetExceptionList(String initialExceptions)
{
initialExceptions = StringUtils.trimToEmpty(initialExceptions);
final StringBuilder exceptionList = new StringBuilder(initialExceptions);
// TODO getExceptions = Collection<ClassifierFacade> or <ModelElementFacade>?
final Collection exceptions = this.getExceptions();
if (exceptions != null && !exceptions.isEmpty())
{
if (StringUtils.isNotBlank(initialExceptions))
{
exceptionList.append(", ");
}
final Iterator exceptionIt = exceptions.iterator();
while (exceptionIt.hasNext())
{
final ModelElementFacade exception = (ModelElementFacade)exceptionIt.next();
exceptionList.append(exception.getFullyQualifiedName());
if (exceptionIt.hasNext())
{
exceptionList.append(", ");
}
}
}
return exceptionList.toString();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getSignature(boolean)
*/
@Override
protected String handleGetSignature(final boolean withArgumentNames)
{
return MetafacadeUtils.getSignature(
this.handleGetName(),
this.getArguments(),
withArgumentNames,
null);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getTypedArgumentList(String)
*/
@Override
protected String handleGetTypedArgumentList(final String modifier)
{
return MetafacadeUtils.getTypedArgumentList(
this.getArguments(),
true,
modifier);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getSignature(String)
*/
@Override
protected String handleGetSignature(final String argumentModifier)
{
return MetafacadeUtils.getSignature(
this.handleGetName(),
this.getArguments(),
true,
argumentModifier);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getOwner()
*/
@Override
protected Object handleGetOwner()
{
Object obj = null;
// Fix from UML2, no longer calls getOwner to get the owning Class
if (this.metaObject.getInterface()!=null)
{
obj = this.metaObject.getInterface();
}
else if (this.metaObject.getDatatype()!=null)
{
obj = this.metaObject.getDatatype();
}
else
{
obj = this.metaObject.getClass_();
}
return obj;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getParameters()
*/
@Override
protected Collection<Parameter> handleGetParameters()
{
final Collection<Parameter> params = new ArrayList<Parameter>(this.metaObject.getOwnedParameters());
params.add(this.metaObject.getReturnResult());
CollectionUtils.filter(
params,
new Predicate()
{
public boolean evaluate(final Object object)
{
return object != null && !((Parameter)object).isException();
}
});
return params;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getReturnType()
*/
@Override
protected Type handleGetReturnType()
{
return this.metaObject.getType();
}
/**
* @return this.getReturnType().getFullyQualifiedName()
* @see org.andromda.metafacades.uml.OperationFacade#getGetterSetterReturnTypeName()
*/
protected String handleGetGetterSetterReturnTypeName()
{
String name = null;
final ClassifierFacade returnType = this.getReturnType();
if (returnType!=null && (this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED))
{
final TypeMappings mappings = this.getLanguageMappings();
name =
this.handleIsOrdered() ? mappings.getTo(UMLProfile.LIST_TYPE_NAME)
: mappings.getTo(UMLProfile.COLLECTION_TYPE_NAME);
// set this attribute's type as a template parameter if required
if (BooleanUtils.toBoolean(
ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING))))
{
String type = returnType.getFullyQualifiedName();
if (returnType.isPrimitive())
{
// Can't template primitive values, Objects only. Convert to wrapped.
type = this.getReturnType().getWrapperName();
}
// Allow List<Type[]> implementations.
/*// Don't apply templating to modeled array types
if (returnType.isArrayType())
{
type = type.substring(0, type.length()-2);
}*/
/*Collection<GeneralizableElementFacade> specializations = returnType.getAllSpecializations();
if ((specializations != null && !specializations.isEmpty()))
{
name += "<? extends " + type + '>';
}
else
{*/
name += '<' + type + '>';
//}
}
}
if (name == null && returnType != null)
{
name = returnType.getFullyQualifiedName();
// Special case: lower bound overrides primitive/wrapped type declaration
// TODO Apply to all primitive types, not just booleans. This is a special case because of is/get Getters.
if (this.getReturnType().isBooleanType())
{
if (this.getReturnType().isPrimitive() && (this.getLower() < 1))
{
// Type is optional, should not be primitive
name = StringUtils.capitalize(name);
}
else if (!this.getReturnType().isPrimitive() && this.getLower() > 0)
{
// Type is required, should not be wrapped
name = StringUtils.uncapitalize(name);
}
}
}
return name;
}
/**
* @return this.metaObject.isOrdered()
* @see org.andromda.metafacades.uml.OperationFacade#isOrdered()
*/
protected boolean handleIsOrdered()
{
return this.metaObject.isOrdered();
}
/**
* @return this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED
* @see org.andromda.metafacades.uml.OperationFacade#isMany()
*/
protected boolean handleIsMany()
{
// Because of MD11.5 (their multiplicity are String), we cannot use
// isMultiValued()
// RJF3 True if either the operation is many or the return parameter is many
final ParameterFacade returnParameter = this.getReturnParameter();
// Parameter or parameter type may be null during model validation
final boolean returnMany = returnParameter!=null && returnParameter.getType()!=null &&
(returnParameter.getUpper() > 1 ||
returnParameter.getUpper() == LiteralUnlimitedNatural.UNLIMITED
|| returnParameter.getType().isArrayType()
|| returnParameter.getType().isCollectionType());
return returnMany || this.getUpper() > 1 || this.getUpper() == LiteralUnlimitedNatural.UNLIMITED;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getArguments()
*/
@Override
protected Collection<Parameter> handleGetArguments()
{
final Collection<Parameter> arguments = new ArrayList<Parameter>(this.metaObject.getOwnedParameters());
CollectionUtils.filter(
arguments,
new Predicate()
{
public boolean evaluate(final Object object)
{
final Parameter param = (Parameter)object;
return !param.getDirection().equals(ParameterDirectionKind.RETURN_LITERAL) && !param.isException();
}
});
return arguments;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getPreconditions()
*/
@Override
protected Collection<ConstraintFacade> handleGetPreconditions()
{
return this.handleGetConstraints(ExpressionKinds.PRE);
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getPostconditions()
*/
@Override
protected Collection<ConstraintFacade> handleGetPostconditions()
{
return this.handleGetConstraints(ExpressionKinds.POST);
}
/**
* @see org.andromda.core.metafacade.MetafacadeBase#getValidationOwner()
*/
@Override
public Object getValidationOwner()
{
return this.getOwner();
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#findParameter(String)
*/
@Override
protected ParameterFacade handleFindParameter(final String name)
{
return (ParameterFacade)this.shieldedElement(this.metaObject.getOwnedParameter(name, null));
}
/**
* Get the UML upper multiplicity Not implemented for UML1.4
* @return multiplicity upperBound
*/
@Override
protected int handleGetUpper()
{
// MD11.5 Exports multiplicity as String
return this.metaObject.getUpper();
//return UmlUtilities.parseMultiplicity(this.metaObject.getUpper(), 1);
}
/**
* Get the UML lower multiplicity Not implemented for UML1.4
* @return multiplicity lowerBound
*/
@Override
protected int handleGetLower()
{
// MD11.5 Exports multiplicity as String
return this.metaObject.getLower();
//return UmlUtilities.parseLowerMultiplicity(this.metaObject.getLower(),
// (ClassifierFacade) this.getReturnType(), "0");
// ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY)));
}
/**
* Get the first UML2 ReturnResult parameter. Not implemented for UML1.4
* @return ReturnResult parameter
*/
@Override
public ParameterFacade handleGetReturnParameter()
{
return (ParameterFacade)this.shieldedElement(this.metaObject.getReturnResult());
}
/*
* Override to change private to public, since it makes no sense to have private operations in generated code
* Allows for protected, package level visibility of operations in the model
* @return String visibility
@Override
protected String handleGetVisibility()
{
String visibility = super.handleGetVisibility();
if (visibility==null || visibility.equals("private"))
{
visibility = "public";
}
return visibility;
}
*/
/**
* @see org.andromda.metafacades.uml.OperationFacade#isOverriding()
*/
@Override
protected boolean handleIsOverriding()
{
return this.getOverriddenOperation() != null;
}
/**
* @see org.andromda.metafacades.uml.OperationFacade#getOverriddenOperation()
*/
@Override
protected OperationFacade handleGetOverriddenOperation()
{
OperationFacade overriddenOperation = null;
final String signature = this.getSignature(false);
ClassifierFacade ancestor = this.getOwner().getSuperClass();
while (overriddenOperation == null && ancestor != null)
{
for (final Iterator<OperationFacade> operationIterator = ancestor.getOperations().iterator();
overriddenOperation == null && operationIterator.hasNext();)
{
final OperationFacade ancestorOperation = operationIterator.next();
if (signature.equals(ancestorOperation.getSignature(false)))
{
overriddenOperation = ancestorOperation;
}
}
ancestor = ancestor.getSuperClass();
}
return overriddenOperation;
}
/**
* @return this.metaObject.isLeaf()
* @see org.andromda.metafacades.uml.OperationFacade#isLeaf()
*/
protected boolean handleIsLeaf()
{
return this.metaObject.isLeaf();
}
/**
* @return this.metaObject.isUnique()
* @see org.andromda.metafacades.uml.OperationFacade#isUnique()
*/
protected boolean handleIsUnique()
{
return this.metaObject.isUnique();
}
}