ContextElementFinder.java
package org.andromda.translation.ocl.testsuite;
import java.util.ArrayList;
import java.util.Collection;
import org.andromda.core.common.ExceptionUtils;
import org.andromda.core.metafacade.ModelAccessFacade;
import org.andromda.core.translation.Expression;
import org.andromda.metafacades.uml.ClassifierFacade;
import org.andromda.metafacades.uml.ModelElementFacade;
import org.andromda.metafacades.uml.OperationFacade;
import org.andromda.metafacades.uml.ParameterFacade;
import org.andromda.translation.ocl.BaseTranslator;
import org.andromda.translation.ocl.node.AOperationContextDeclaration;
import org.andromda.translation.ocl.node.POperation;
import org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils;
import org.andromda.translation.ocl.syntax.OperationDeclaration;
import org.andromda.translation.ocl.syntax.VariableDeclaration;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
/**
* Finds the context element defined in the OCL expression.
*
* @author Chad Brandon
*/
public class ContextElementFinder
extends BaseTranslator
{
private ModelAccessFacade model;
/**
* The Constructor which takes <code>model</code>, which is an instance of ModelAccessFacade that will allow us to
* search the model for the context element.
*
* @param model the ModelAccessFacade to search.
*/
public ContextElementFinder(ModelAccessFacade model)
{
ExceptionUtils.checkNull("model", model);
this.model = model;
}
/**
* Hide the default constructor
*/
@SuppressWarnings("unused")
private ContextElementFinder()
{
}
/**
* The operation that is set if the context of the constraint happens to be an operation.
*/
protected OperationDeclaration operation = null;
/**
* The found context type.
*/
private Object contextElement = null;
/**
* @param declaration
* @see org.andromda.translation.ocl.syntax.ConcreteSyntaxUtils#getOperationDeclaration(POperation)
*/
public void inAOperationContextDeclaration(AOperationContextDeclaration declaration)
{
super.inAOperationContextDeclaration(declaration);
if (declaration != null)
{
operation = ConcreteSyntaxUtils.getOperationDeclaration(declaration.getOperation());
}
}
/**
* We use the postProcess method to retrieve the contextType from the expression and then find the actual model
* element using metafacades.
*
* @see org.andromda.translation.ocl.BaseTranslator#postProcess()
*/
@Override
public void postProcess()
{
Expression expression = this.getExpression();
if (expression != null)
{
String contextElementName = expression.getContextElement();
this.contextElement = this.findModelElement(contextElementName.replaceAll("::", "\\."));
if (this.contextElement != null)
{
logger.info("found context element --> '" + contextElementName + '\'');
} else
{
logger.info("Could not find model element --> '" + contextElementName + '\'');
}
if (this.contextElement != null && this.operation != null &&
ClassifierFacade.class.isAssignableFrom(contextElement.getClass()))
{
ClassifierFacade type = (ClassifierFacade) this.contextElement;
Collection operations = type.getOperations();
this.contextElement = CollectionUtils.find(operations, new OperationFinder());
if (this.contextElement == null)
{
throw new ContextElementFinderException("No operation matching '" + operation +
"' could be found on element --> '" + contextElementName + "', please check your model");
}
// if we only have one operation then we just set that
// as the context element, otherwise we'll need to figure
// out which operation is the context operation by checking
// the arguments.
if (operations.size() == 1)
{
this.contextElement = operations.iterator().next();
} else
{
// now find the correct operation since there are
// more than one with the same name
}
}
}
}
private final class OperationFinder
implements Predicate
{
public boolean evaluate(Object object)
{
OperationFacade facadeOperation = (OperationFacade) object;
boolean valid = StringUtils.trimToEmpty(facadeOperation.getName()).equals(
StringUtils.trimToEmpty(operation.getName()));
// if we've found an operation with a matching name
// check the parameters
if (valid)
{
valid = argumentsMatch(operation, facadeOperation);
}
return valid;
}
}
/**
* Returns true if the arguments contained within <code>oclOperation</code> and <code>facadeOperation</code> match,
* false otherwise.
*
* @param oclOperation an OCL Operation
* @param facadeOperation a metafacade Operation
* @return boolean whether the arguments match.
*/
protected boolean argumentsMatch(OperationDeclaration oclOperation, OperationFacade facadeOperation)
{
boolean argumentsMatch = this.argumentCountsMatch(oclOperation, facadeOperation);
if (argumentsMatch)
{
argumentsMatch = this.argumentNamesMatch(oclOperation, facadeOperation);
}
return argumentsMatch;
}
/**
* Returns true if the number of arguments contained within <code>oclOperation</code> and
* <code>facadeOperation</code> match, false otherwise.
*
* @param oclOperation an OCL Operation
* @param facadeOperation a metafacade Operation
* @return boolean whether the count of the arguments match.
*/
private boolean argumentCountsMatch(OperationDeclaration oclOperation, OperationFacade facadeOperation)
{
ExceptionUtils.checkNull("oclOperation", oclOperation);
ExceptionUtils.checkNull("facadeOperation", facadeOperation);
VariableDeclaration[] expressionOpArgs = oclOperation.getArguments();
Collection facadeOpArgs = facadeOperation.getArguments();
boolean countsMatch = (expressionOpArgs == null || expressionOpArgs.length == 0) &&
(facadeOpArgs == null || facadeOpArgs.isEmpty());
if (!countsMatch)
{
countsMatch = expressionOpArgs != null && facadeOpArgs != null &&
expressionOpArgs.length == facadeOpArgs.size();
}
return countsMatch;
}
/**
* Returns true if the argument names contained within <code>oclOperation</code> and <code>facadeOperation</code>
* match, false otherwise.
*
* @param oclOperation an OCL Operation
* @param facadeOperation a metafacade Operation
* @return boolean whether the arg names match or not.
*/
private boolean argumentNamesMatch(OperationDeclaration oclOperation, OperationFacade facadeOperation)
{
ExceptionUtils.checkNull("oclOperation", oclOperation);
ExceptionUtils.checkNull("facadeOperation", facadeOperation);
Collection<ParameterFacade> facadeOpArguments = facadeOperation.getArguments();
VariableDeclaration[] expressionOpArgs = oclOperation.getArguments();
Collection<String> expressionArgNames = new ArrayList<String>();
if (expressionOpArgs != null)
{
for (VariableDeclaration expressionOpArg : expressionOpArgs)
{
expressionArgNames.add(expressionOpArg.getName());
}
}
Collection<String> facadeArgNames = new ArrayList<String>();
if (facadeOpArguments != null)
{
for (ParameterFacade facadeArg : facadeOpArguments)
{
facadeArgNames.add(facadeArg.getName());
}
}
return CollectionUtils.isEqualCollection(expressionArgNames, facadeArgNames);
}
/**
* Finds the model element with the given <code>modelElementName</code>. Will find either the non qualified name or
* qualified name. If more than one model element is found with the non qualified name an exception will be thrown.
*
* @param modelElementName
* @return Object the found model element
*/
private Object findModelElement(final String modelElementName)
{
Object modelElement = null;
Collection modelElements = this.model.getModelElements();
CollectionUtils.filter(modelElements, new Predicate()
{
public boolean evaluate(Object object)
{
boolean valid = false;
if (ModelElementFacade.class.isAssignableFrom(object.getClass()))
{
ModelElementFacade modelElement = (ModelElementFacade) object;
String elementName = StringUtils.trimToEmpty(modelElement.getName());
String name = StringUtils.trimToEmpty(modelElementName);
valid = elementName.equals(name);
if (!valid)
{
elementName = StringUtils.trimToEmpty(modelElement.getFullyQualifiedName());
valid = elementName.equals(name);
}
}
return valid;
}
});
if (modelElements.size() > 1)
{
throw new ContextElementFinderException("More than one element named '" + modelElementName +
"' was found within your model," + " please give the fully qualified name");
} else if (modelElements.size() == 1)
{
modelElement = modelElements.iterator().next();
}
return modelElement;
}
/**
* Returns the context element found in the model from the expression.
*
* @return the context type as a ModelElementFacade.
*/
public Object getContextElement()
{
return this.contextElement;
}
}