//Source file: H:\\temp\\generated\\modTransf\\rules\\core\\RuleCallAction.java
package modTransf.rules.core;
import modTransf.engine.Arguments;
import modTransf.engine.RuleContext;
import modTransf.engine.TransformationException;
import modTransf.engine.EngineLifeCycle;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collection;
import modTransf.engine.Rule;
import modTransf.engine.ParameterDescriptor;
import modTransf.engine.EngineException;
import modTransf.engine.ArrayArguments;
import modTransf.util.TransposedListView;
/**
* This action allows to call a rule.
* This action can be used as action and as a guard. A call to a rule return a
* boolean indicating if the rule has been executed.
* The parameter values are compute, and passed as arguments to the rule. Then the
* rule is called. Finally, the output parameter values (as defined by the called
* rule) are set in the current rule.
* If one parameter denotes a collection, the rule is called with each possible
* combination of the cartesian product of the arguments.
* @todo Check the parameter count (be care of problems because some rules are not
* initialized).
* @todo Be able to create an instance and call it dynamically. call( ruleName, argumentExpr, context)
* setParameterExpression( String params, RuleContext context )
*/
public class RuleCallAction implements GuardClause, ActionClause
{
/**
* The rule to call.
*/
protected String ruleName;
/**
* The rule to call !
*/
protected Rule rule;
/**
* The parameters expressions to get/set the rule arguments.
*/
protected List parameters = new ArrayList();
private boolean parameterChecked = false;
/**
* Constructor.
* This constructor is to be called by subclasses. The subclasses should ensure
* that the ruleName is set before calling "callRule()".
*/
protected RuleCallAction()
{
}
/**
* Constructor.
*/
public RuleCallAction(String ruleName)
{
this.ruleName = ruleName;
}
/**
* Sets the value of the rule property.
*
* @param aRule the new value of the rule property
*/
public void setRuleName(String aRule)
{
ruleName = aRule;
}
/**
* @param expr
*/
public void addParameterExpression(ParameterExpression expr)
{
parameters.add(expr);
}
/**
* Get the list of parameter expressions.
* @return List
*/
public List getParameterExpressions()
{
return parameters;
}
/**
* Call the rule. Retrieve the arguments by using the associated expressions,
* then call the rule in a new context. If the arguments contain one or more
* collection, the rule is called for each possible combination. Each result
* is stored in the variable denoted by the expressions.
* The engineStart() method should have been called prior to any call to this method.
*
* @param currentBean The current bean (used in 'this' expressions).
* @param context RuleContext
* @return boolean
* @throws TransformationException
*/
protected boolean callRule( RuleContext context )
throws TransformationException
{
//System.out.println("RuleCall.callRule( '" + ruleName + "') ");
// Check if the rule is set. If not, call the engineStart() method.
// This should normally be done by the engine at startup. The extra call is made
// for convenience.
if( rule==null)
{
try
{
engineStart(context);
}
catch(EngineException ex)
{
throw new TransformationException(ex);
}
}
boolean isCalled = false;
// Get the in arguments
ExtendedArguments args = getInArguments(context);
//System.out.println("RuleCall.callRule( '" + ruleName + "', "+args + ") ");
// call the rule for each combination in case of collections. The result
// saving should be done for each call.
// call the rule
if( args.containsCollection() )
{
// Iterate on all combinations
ArgumentsIterator combinationsIter = new ArgumentsIterator( args );
List res = new ArrayList();
while(combinationsIter.hasNext() )
{
Arguments curArgs = combinationsIter.nextArguments();
if( executeRuleInNewContext( curArgs, context ) )
{
//System.out.println("add args '" + curArgs + "'." );
res.add( curArgs );
isCalled = true;
}
}
// Save results
if( isCalled )
storeOutArg( toArguments(res), context );
}
else
{ // No iteration, use args directly
if( executeRuleInNewContext( args, context ) )
{
storeOutArg(args, context);
isCalled = true;
}
}
return isCalled;
}
/**
* Transform a List of Arguments into an Arguments of List
*
* @param res List
* @return Arguments
*/
protected Arguments toArguments(List listOfLists)
{
return new TransposedListView(listOfLists);
}
/**
* Get the in argument values from the call expressions.
* @param context RuleContext
* @throws TransformationException
*/
private ExtendedArguments getInArguments(RuleContext context)
throws TransformationException
{
// Get in parameter values
// Iterate on expressions and build Arguments. Only in arguments are read.
ExtendedArguments args = new ExtendedArguments(parameters.size());
// Iterate on call expressions
Iterator paramIter = parameters.iterator();
// Rule domains are used to check the direction
Iterator descIter = rule.getParameterDescriptors().iterator();
while(paramIter.hasNext())
{
ParameterDescriptor paramDesc = (ParameterDescriptor)descIter.next();
ParameterExpression exprValue = (ParameterExpression)paramIter.next();
//System.out.println( ruleName + ".desc."+paramDesc.isIn(context)+ " ");
if( paramDesc.isIn(context) )
{
Object arg = exprValue.getValue(context);
args.add(arg);
}
else
{ // out parameter, put an null value.
args.add(null);
}
}
return args;
}
/**
* Try to Execute the rule with the provided args. Create a new context for
* the execution.
* @param args Arguments
* @param context RuleContext
* @return boolean
*/
protected boolean executeRuleInNewContext( Arguments args, RuleContext context )
throws TransformationException
{
context.enterLocalContext();
try
{
return rule.execute(args, context);
}
finally
{
context.exitLocalContext();
}
}
/**
* Store the out arguments in the context.
* @param args Arguments
* @param context RuleContext
* @throws TransformationException
*/
protected void storeOutArg( Arguments args, RuleContext context )
throws TransformationException
{
if( args.size() != parameters.size() )
throw new TransformationException("Returned args count should match parameter descriptor count. Expected "
+ parameters.size() + ", found " + args.size() + ".");
// Iterate on call parameters expressions
Iterator paramIter = parameters.iterator();
// Iterate on rule domains to find the direction
Iterator descIter = rule.getParameterDescriptors().iterator();
// Iterate on argument values
Iterator argsIter = args.iterator();
while(paramIter.hasNext())
{
ParameterDescriptor paramDesc = (ParameterDescriptor)descIter.next();
ParameterExpression exprValue = (ParameterExpression)paramIter.next();
Object value = argsIter.next();
//System.out.println("store result (isOut=" +paramDesc.isOut(context) + ") '" + value + "'" );
if( paramDesc.isOut(context) )
{
if( value != null )
exprValue.setValue( value, context);
}
}
}
/**
* @param args
* @param context
*/
public void setUpLocalVariables(Object args, RuleContext context)
{
}
/**
* @param object
* @param request
* @return boolean
* @throws ispuml.mdaTransformation.TransformationException
*/
public boolean isAllowed(Object object, RuleContext request)
throws TransformationException
{
return callRule(request);
}
/**
* @param context
*/
public void engineStart(RuleContext context)
throws EngineException
{
rule = context.getTransformation().getRuleSet().getRule(ruleName);
if( rule == null )
throw new EngineException("No rule found under name '" + ruleName + "'." );
// Check number of parameters
checkParameterCount();
// call life cycle on parameters if needed
Iterator iter = parameters.iterator();
while( iter.hasNext() )
{
Object param = iter.next();
if( param instanceof EngineLifeCycle )
((EngineLifeCycle)param).engineStart(context);
}
}
/**
* Check if the parameter count of the rule to call is compatible with the
* number of declared parameters.
* This method can only be called if the rule to call is already initialized.
* So, it is not always possible to call it from engineStart().
* @throws EngineException
*/
private void checkParameterCount()
throws EngineException
{
List descr = rule.getParameterDescriptors();
// Try to get the desc of the rule to call. If we can't get it, abort.
// This happen when the rule is not yet initialized.
if( descr == null )
return;
int requiredParameterCount = descr.size();
if( requiredParameterCount != parameters.size() )
throw new EngineException( "RuleCall - Rule '" + ruleName + "' requires "
+ requiredParameterCount +" parameters. "
+ parameters.size() + " are provided." );
}
/**
* @param context
*/
public void engineFinish(RuleContext context)
throws EngineException
{
// call life cycle on parameters if needed
Iterator iter = parameters.iterator();
while( iter.hasNext() )
{
Object param = iter.next();
if( param instanceof EngineLifeCycle )
((EngineLifeCycle)param).engineFinish(context);
}
}
/**
* execute
*
* @param bean Object
* @param request RuleContext
* @return Object
*/
public Object execute(Object bean, RuleContext request)
throws TransformationException
{
return new Boolean (callRule(request));
}
/**
* An Arguments knowing if it contains collections.
*/
private class ExtendedArguments extends ArrayArguments
{
private boolean containsCollection = false;
public ExtendedArguments()
{
super();
}
public ExtendedArguments(Collection coll)
{
super(coll);
}
public ExtendedArguments(int initialCapacity)
{
super(initialCapacity);
}
public boolean containsCollection()
{
return containsCollection;
}
public void setContainsCollection(boolean contains)
{
containsCollection = contains;
}
private void checkCollection( Object object )
{
if( object instanceof Collection )
containsCollection = true;
}
public boolean add( Object object )
{
checkCollection(object);
return super.add(object);
}
public void add( int index, Object object )
{
checkCollection(object);
super.add(index, object);
}
}
}