/*
* Copyright 2012 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.workbench.models.commons.backend.rule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.drools.compiler.compiler.DrlParser;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.lang.descr.AccumulateDescr;
import org.drools.compiler.lang.descr.AndDescr;
import org.drools.compiler.lang.descr.AnnotationDescr;
import org.drools.compiler.lang.descr.AttributeDescr;
import org.drools.compiler.lang.descr.BaseDescr;
import org.drools.compiler.lang.descr.BehaviorDescr;
import org.drools.compiler.lang.descr.CollectDescr;
import org.drools.compiler.lang.descr.ConditionalElementDescr;
import org.drools.compiler.lang.descr.EntryPointDescr;
import org.drools.compiler.lang.descr.EvalDescr;
import org.drools.compiler.lang.descr.ExprConstraintDescr;
import org.drools.compiler.lang.descr.FromDescr;
import org.drools.compiler.lang.descr.GlobalDescr;
import org.drools.compiler.lang.descr.NotDescr;
import org.drools.compiler.lang.descr.OrDescr;
import org.drools.compiler.lang.descr.PackageDescr;
import org.drools.compiler.lang.descr.PatternDescr;
import org.drools.compiler.lang.descr.PatternSourceDescr;
import org.drools.compiler.lang.descr.RuleDescr;
import org.drools.core.base.evaluators.EvaluatorRegistry;
import org.drools.core.base.evaluators.Operator;
import org.drools.core.util.ReflectiveVisitor;
import org.drools.workbench.models.commons.backend.imports.ImportsParser;
import org.drools.workbench.models.commons.backend.imports.ImportsWriter;
import org.drools.workbench.models.commons.backend.packages.PackageNameParser;
import org.drools.workbench.models.commons.backend.packages.PackageNameWriter;
import org.drools.workbench.models.commons.backend.rule.context.LHSGeneratorContext;
import org.drools.workbench.models.commons.backend.rule.context.LHSGeneratorContextFactory;
import org.drools.workbench.models.commons.backend.rule.context.RHSGeneratorContext;
import org.drools.workbench.models.commons.backend.rule.context.RHSGeneratorContextFactory;
import org.drools.workbench.models.datamodel.imports.Import;
import org.drools.workbench.models.datamodel.imports.Imports;
import org.drools.workbench.models.datamodel.oracle.DataType;
import org.drools.workbench.models.datamodel.oracle.MethodInfo;
import org.drools.workbench.models.datamodel.oracle.ModelField;
import org.drools.workbench.models.datamodel.oracle.OperatorsOracle;
import org.drools.workbench.models.datamodel.oracle.PackageDataModelOracle;
import org.drools.workbench.models.datamodel.rule.ActionCallMethod;
import org.drools.workbench.models.datamodel.rule.ActionExecuteWorkItem;
import org.drools.workbench.models.datamodel.rule.ActionFieldFunction;
import org.drools.workbench.models.datamodel.rule.ActionFieldList;
import org.drools.workbench.models.datamodel.rule.ActionFieldValue;
import org.drools.workbench.models.datamodel.rule.ActionGlobalCollectionAdd;
import org.drools.workbench.models.datamodel.rule.ActionInsertFact;
import org.drools.workbench.models.datamodel.rule.ActionInsertLogicalFact;
import org.drools.workbench.models.datamodel.rule.ActionRetractFact;
import org.drools.workbench.models.datamodel.rule.ActionSetField;
import org.drools.workbench.models.datamodel.rule.ActionUpdateField;
import org.drools.workbench.models.datamodel.rule.ActionWorkItemFieldValue;
import org.drools.workbench.models.datamodel.rule.BaseSingleFieldConstraint;
import org.drools.workbench.models.datamodel.rule.CEPWindow;
import org.drools.workbench.models.datamodel.rule.CompositeFactPattern;
import org.drools.workbench.models.datamodel.rule.CompositeFieldConstraint;
import org.drools.workbench.models.datamodel.rule.ConnectiveConstraint;
import org.drools.workbench.models.datamodel.rule.DSLSentence;
import org.drools.workbench.models.datamodel.rule.ExpressionCollection;
import org.drools.workbench.models.datamodel.rule.ExpressionField;
import org.drools.workbench.models.datamodel.rule.ExpressionFormLine;
import org.drools.workbench.models.datamodel.rule.ExpressionMethod;
import org.drools.workbench.models.datamodel.rule.ExpressionMethodParameter;
import org.drools.workbench.models.datamodel.rule.ExpressionPart;
import org.drools.workbench.models.datamodel.rule.ExpressionText;
import org.drools.workbench.models.datamodel.rule.ExpressionUnboundFact;
import org.drools.workbench.models.datamodel.rule.ExpressionVariable;
import org.drools.workbench.models.datamodel.rule.FactPattern;
import org.drools.workbench.models.datamodel.rule.FieldConstraint;
import org.drools.workbench.models.datamodel.rule.FieldNature;
import org.drools.workbench.models.datamodel.rule.FieldNatureType;
import org.drools.workbench.models.datamodel.rule.FreeFormLine;
import org.drools.workbench.models.datamodel.rule.FromAccumulateCompositeFactPattern;
import org.drools.workbench.models.datamodel.rule.FromCollectCompositeFactPattern;
import org.drools.workbench.models.datamodel.rule.FromCompositeFactPattern;
import org.drools.workbench.models.datamodel.rule.FromEntryPointFactPattern;
import org.drools.workbench.models.datamodel.rule.IAction;
import org.drools.workbench.models.datamodel.rule.IFactPattern;
import org.drools.workbench.models.datamodel.rule.IPattern;
import org.drools.workbench.models.datamodel.rule.RuleAttribute;
import org.drools.workbench.models.datamodel.rule.RuleMetadata;
import org.drools.workbench.models.datamodel.rule.RuleModel;
import org.drools.workbench.models.datamodel.rule.SingleFieldConstraint;
import org.drools.workbench.models.datamodel.rule.SingleFieldConstraintEBLeftSide;
import org.drools.workbench.models.datamodel.rule.builder.DRLConstraintValueBuilder;
import org.drools.workbench.models.datamodel.rule.visitors.ToStringExpressionVisitor;
import org.drools.workbench.models.datamodel.workitems.HasBinding;
import org.drools.workbench.models.datamodel.workitems.PortableBooleanParameterDefinition;
import org.drools.workbench.models.datamodel.workitems.PortableFloatParameterDefinition;
import org.drools.workbench.models.datamodel.workitems.PortableIntegerParameterDefinition;
import org.drools.workbench.models.datamodel.workitems.PortableObjectParameterDefinition;
import org.drools.workbench.models.datamodel.workitems.PortableParameterDefinition;
import org.drools.workbench.models.datamodel.workitems.PortableStringParameterDefinition;
import org.drools.workbench.models.datamodel.workitems.PortableWorkDefinition;
import static org.drools.core.util.StringUtils.*;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.*;
/**
* This class persists the rule model to DRL and back
*/
public class RuleModelDRLPersistenceImpl
implements
RuleModelPersistence {
private static final String WORKITEM_PREFIX = "wi";
private static final RuleModelPersistence INSTANCE = new RuleModelDRLPersistenceImpl();
//This is the default dialect for rules not specifying one explicitly
protected DRLConstraintValueBuilder constraintValueBuilder = DRLConstraintValueBuilder.getBuilder( DRLConstraintValueBuilder.DEFAULT_DIALECT );
//Keep a record of all variable bindings for Actions that depend on them
protected Map<String, IFactPattern> bindingsPatterns;
protected Map<String, FieldConstraint> bindingsFields;
protected RuleModelDRLPersistenceImpl() {
// register custom evaluators
new EvaluatorRegistry( getClass().getClassLoader() );
}
public static RuleModelPersistence getInstance() {
return INSTANCE;
}
/*
* (non-Javadoc)
* @see
* org.drools.ide.common.server.util.RuleModelPersistence#marshal(org.drools.guvnor
* .client.modeldriven.brl.RuleModel)
*/
public String marshal( final RuleModel model ) {
return marshalRule( model );
}
protected String marshalRule( final RuleModel model ) {
boolean isDSLEnhanced = model.hasDSLSentences();
bindingsPatterns = new HashMap<String, IFactPattern>();
bindingsFields = new HashMap<String, FieldConstraint>();
fixActionInsertFactBindings( model.rhs );
StringBuilder buf = new StringBuilder();
//Build rule
this.marshalPackageHeader( model,
buf );
this.marshalRuleHeader( model,
buf );
this.marshalMetadata( buf,
model );
this.marshalAttributes( buf,
model );
buf.append( "\twhen\n" );
this.marshalLHS( buf,
model,
isDSLEnhanced,
new LHSGeneratorContextFactory() );
buf.append( "\tthen\n" );
this.marshalRHS( buf,
model,
isDSLEnhanced,
new RHSGeneratorContextFactory() );
this.marshalFooter( buf );
return buf.toString();
}
protected void fixActionInsertFactBindings( final IAction[] rhs ) {
final Set<String> existingBindings = extractExistingActionBindings( rhs );
for ( IAction action : rhs ) {
if ( action instanceof ActionInsertFact ) {
final ActionInsertFact aif = (ActionInsertFact) action;
if ( aif.getFieldValues().length > 0 && aif.getBoundName() == null ) {
int idx = 0;
String binding = "fact" + idx;
while ( existingBindings.contains( binding ) ) {
idx++;
binding = "fact" + idx;
}
existingBindings.add( binding );
aif.setBoundName( binding );
}
}
}
}
private Set<String> extractExistingActionBindings( final IAction[] rhs ) {
final Set<String> bindings = new HashSet<String>();
for ( IAction action : rhs ) {
if ( action instanceof ActionInsertFact ) {
final ActionInsertFact aif = (ActionInsertFact) action;
if ( aif.getBoundName() != null ) {
bindings.add( aif.getBoundName() );
}
}
}
return bindings;
}
protected void marshalFooter( final StringBuilder buf ) {
buf.append( "end\n" );
}
//Append package name and imports to DRL
protected void marshalPackageHeader( final RuleModel model,
final StringBuilder buf ) {
PackageNameWriter.write( buf,
model );
ImportsWriter.write( buf,
model );
}
//Append rule header
protected void marshalRuleHeader( final RuleModel model,
final StringBuilder buf ) {
buf.append( "rule \"" + marshalRuleName( model ) + "\"" );
if ( null != model.parentName && model.parentName.length() > 0 ) {
buf.append( " extends \"" + model.parentName + "\"\n" );
} else {
buf.append( '\n' );
}
}
protected String marshalRuleName( final RuleModel model ) {
return model.name;
}
/**
* Marshal model attributes
* @param buf
* @param model
*/
protected void marshalAttributes( final StringBuilder buf,
final RuleModel model ) {
boolean hasDialect = false;
for ( int i = 0; i < model.attributes.length; i++ ) {
RuleAttribute attr = model.attributes[ i ];
buf.append( "\t" );
buf.append( attr );
buf.append( "\n" );
if ( attr.getAttributeName().equals( "dialect" ) ) {
constraintValueBuilder = DRLConstraintValueBuilder.getBuilder( attr.getValue() );
hasDialect = true;
}
}
// Un comment below for mvel
if ( !hasDialect ) {
RuleAttribute attr = new RuleAttribute( "dialect",
DRLConstraintValueBuilder.DEFAULT_DIALECT );
buf.append( "\t" );
buf.append( attr );
buf.append( "\n" );
}
}
/**
* Marshal model metadata
* @param buf
* @param model
*/
protected void marshalMetadata( final StringBuilder buf,
final RuleModel model ) {
if ( model.metadataList != null ) {
for ( int i = 0; i < model.metadataList.length; i++ ) {
buf.append( "\t" ).append( model.metadataList[ i ] ).append( "\n" );
}
}
}
/**
* Marshal LHS patterns
* @param buf
* @param model
*/
protected void marshalLHS( final StringBuilder buf,
final RuleModel model,
final boolean isDSLEnhanced,
final LHSGeneratorContextFactory generatorContextFactory ) {
String indentation = "\t\t";
String nestedIndentation = indentation;
boolean isNegated = model.isNegated();
if ( model.lhs != null ) {
if ( isNegated ) {
nestedIndentation += "\t";
buf.append( indentation );
buf.append( "not (\n" );
}
LHSPatternVisitor visitor = getLHSPatternVisitor( isDSLEnhanced,
buf,
nestedIndentation,
isNegated,
generatorContextFactory );
for ( IPattern cond : model.lhs ) {
visitor.visit( cond );
}
if ( model.isNegated() ) {
//Delete the spurious " and ", added by LHSPatternVisitor.visitFactPattern, when the rule is negated
buf.delete( buf.length() - 5,
buf.length() );
buf.append( "\n" );
buf.append( indentation );
buf.append( ")\n" );
}
}
}
protected LHSPatternVisitor getLHSPatternVisitor( final boolean isDSLEnhanced,
final StringBuilder buf,
final String nestedIndentation,
final boolean isNegated,
final LHSGeneratorContextFactory generatorContextFactory ) {
return new LHSPatternVisitor( isDSLEnhanced,
bindingsPatterns,
bindingsFields,
constraintValueBuilder,
generatorContextFactory,
buf,
nestedIndentation,
isNegated );
}
protected void marshalRHS( final StringBuilder buf,
final RuleModel model,
final boolean isDSLEnhanced,
final RHSGeneratorContextFactory generatorContextFactory ) {
String indentation = "\t\t";
if ( model.rhs != null ) {
//Add boiler-plate for actions operating on Dates
Map<String, List<ActionFieldValue>> classes = getRHSClassDependencies( model );
if ( classes.containsKey( DataType.TYPE_DATE ) ) {
buf.append( indentation );
buf.append( "java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(\"" + System.getProperty( "drools.dateformat" ) + "\");\n" );
}
//Add boiler-plate for actions operating on WorkItems
if ( !getRHSWorkItemDependencies( model ).isEmpty() ) {
buf.append( indentation );
buf.append( "org.drools.core.process.instance.WorkItemManager wim = (org.drools.core.process.instance.WorkItemManager) drools.getWorkingMemory().getWorkItemManager();\n" );
}
//Marshall the model itself
RHSActionVisitor actionVisitor = getRHSActionVisitor( isDSLEnhanced,
buf,
indentation,
generatorContextFactory );
//Reconcile ActionSetField and ActionUpdateField calls
final List<IAction> actions = new ArrayList<IAction>();
for ( IAction action : model.rhs ) {
if ( action instanceof ActionSetField ) {
final ActionSetField asf = (ActionSetField) action;
final ActionSetFieldWrapper afw = findExistingAction( asf,
actions );
if ( afw == null ) {
actions.add( new ActionSetFieldWrapper( asf,
( asf instanceof ActionUpdateField ) ) );
} else {
final List<ActionFieldValue> existingActionFieldValue = new ArrayList<ActionFieldValue>( Arrays.asList( afw.getAction().getFieldValues() ) );
for ( ActionFieldValue afv : asf.getFieldValues() ) {
existingActionFieldValue.add( afv );
}
final ActionFieldValue[] temp = new ActionFieldValue[ existingActionFieldValue.size() ];
afw.getAction().setFieldValues( existingActionFieldValue.toArray( temp ) );
}
} else {
actions.add( action );
}
}
model.rhs = new IAction[ actions.size() ];
for ( int i = 0; i < actions.size(); i++ ) {
final IAction action = actions.get( i );
if ( action instanceof ActionSetFieldWrapper ) {
model.rhs[ i ] = ( (ActionSetFieldWrapper) action ).getAction();
} else {
model.rhs[ i ] = action;
}
}
//Now generate DRL
for ( IAction action : model.rhs ) {
actionVisitor.visit( action );
}
}
}
private ActionSetFieldWrapper findExistingAction( final ActionSetField asf,
final List<IAction> actions ) {
for ( IAction action : actions ) {
if ( action instanceof ActionSetFieldWrapper ) {
final ActionSetFieldWrapper afw = (ActionSetFieldWrapper) action;
if ( asf.getVariable().equals( afw.getAction().getVariable() ) && ( asf instanceof ActionUpdateField ) == afw.isUpdate() ) {
return afw;
}
}
}
return null;
}
private static class ActionSetFieldWrapper implements IAction {
private final ActionSetField action;
private final boolean isUpdate;
private ActionSetFieldWrapper( final ActionSetField action,
final boolean isUpdate ) {
this.action = clone( action );
this.isUpdate = isUpdate;
}
private ActionSetField getAction() {
return action;
}
private boolean isUpdate() {
return isUpdate;
}
private ActionSetField clone( final ActionSetField action ) {
if ( action instanceof ActionUpdateField ) {
final ActionUpdateField auf = (ActionUpdateField) action;
final ActionUpdateField clone = new ActionUpdateField( auf.getVariable() );
clone.setFieldValues( auf.getFieldValues() );
return clone;
} else if ( action instanceof ActionCallMethod ) {
final ActionCallMethod acm = (ActionCallMethod) action;
final ActionCallMethod clone = new ActionCallMethod( acm.getVariable() );
clone.setState( acm.getState() );
clone.setMethodName( acm.getMethodName() );
clone.setFieldValues( acm.getFieldValues() );
return clone;
} else if ( action instanceof ActionSetField ) {
final ActionSetField clone = new ActionSetField( action.getVariable() );
clone.setFieldValues( action.getFieldValues() );
return clone;
} else {
return action;
}
}
}
protected RHSActionVisitor getRHSActionVisitor( final boolean isDSLEnhanced,
final StringBuilder buf,
final String indentation,
final RHSGeneratorContextFactory generatorContextFactory ) {
return new RHSActionVisitor( isDSLEnhanced,
bindingsPatterns,
bindingsFields,
constraintValueBuilder,
generatorContextFactory,
buf,
indentation );
}
private Map<String, List<ActionFieldValue>> getRHSClassDependencies( final RuleModel model ) {
if ( model != null ) {
RHSClassDependencyVisitor dependencyVisitor = new RHSClassDependencyVisitor();
for ( IAction action : model.rhs ) {
dependencyVisitor.visit( action );
}
return dependencyVisitor.getRHSClasses();
}
Map<String, List<ActionFieldValue>> empty = Collections.emptyMap();
return empty;
}
private List<PortableWorkDefinition> getRHSWorkItemDependencies( final RuleModel model ) {
if ( model != null ) {
List<PortableWorkDefinition> workItems = new ArrayList<PortableWorkDefinition>();
for ( IAction action : model.rhs ) {
if ( action instanceof ActionExecuteWorkItem ) {
workItems.add( ( (ActionExecuteWorkItem) action ).getWorkDefinition() );
}
}
return workItems;
}
List<PortableWorkDefinition> empty = Collections.emptyList();
return empty;
}
public static class LHSPatternVisitor extends ReflectiveVisitor {
protected StringBuilder buf;
private boolean isDSLEnhanced;
private boolean isPatternNegated;
private String indentation;
private Map<String, IFactPattern> bindingsPatterns;
private Map<String, FieldConstraint> bindingsFields;
protected DRLConstraintValueBuilder constraintValueBuilder;
protected LHSGeneratorContextFactory generatorContextFactory;
protected final LHSGeneratorContext rootContext;
public LHSPatternVisitor( final boolean isDSLEnhanced,
final Map<String, IFactPattern> bindingsPatterns,
final Map<String, FieldConstraint> bindingsFields,
final DRLConstraintValueBuilder constraintValueBuilder,
final LHSGeneratorContextFactory generatorContextFactory,
final StringBuilder b,
final String indentation,
final boolean isPatternNegated ) {
this.isDSLEnhanced = isDSLEnhanced;
this.bindingsPatterns = bindingsPatterns;
this.bindingsFields = bindingsFields;
this.constraintValueBuilder = constraintValueBuilder;
this.generatorContextFactory = generatorContextFactory;
this.rootContext = generatorContextFactory.newGeneratorContext();
this.indentation = indentation;
this.isPatternNegated = isPatternNegated;
this.buf = b;
}
protected void preGeneratePattern( final LHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void postGeneratePattern( final LHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void preGenerateNestedConnector( final LHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void postGenerateNestedConnector( final LHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void preGenerateNestedConstraint( final LHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void postGenerateNestedConstraint( final LHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
public void visitFactPattern( final FactPattern pattern ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
// adding passthrough markup
buf.append( ">" );
}
final LHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
pattern );
preGeneratePattern( gctx );
generateFactPattern( pattern,
gctx );
if ( isPatternNegated ) {
buf.append( " and " );
}
postGeneratePattern( gctx );
buf.append( "\n" );
}
public void visitFreeFormLine( final FreeFormLine ffl ) {
if ( ffl.getText() == null ) {
return;
}
String[] lines = ffl.getText().split( "\\n|\\r\\n" );
for ( String line : lines ) {
this.buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
this.buf.append( line + "\n" );
}
}
public void visitCompositeFactPattern( final CompositeFactPattern pattern ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
// adding passthrough markup
buf.append( ">" );
}
if ( CompositeFactPattern.COMPOSITE_TYPE_EXISTS.equals( pattern.getType() ) ) {
renderCompositeFOL( pattern );
} else if ( CompositeFactPattern.COMPOSITE_TYPE_NOT.equals( pattern.getType() ) ) {
renderCompositeFOL( pattern );
} else if ( CompositeFactPattern.COMPOSITE_TYPE_OR.equals( pattern.getType() ) ) {
buf.append( "( " );
if ( pattern.getPatterns() != null ) {
for ( int i = 0; i < pattern.getPatterns().length; i++ ) {
if ( i > 0 ) {
buf.append( " " );
buf.append( pattern.getType() );
buf.append( " " );
}
renderSubPattern( pattern,
i );
}
}
buf.append( " )\n" );
}
}
public void visitFromCompositeFactPattern( final FromCompositeFactPattern pattern ) {
visitFromCompositeFactPattern( pattern,
false );
}
public void visitFromCompositeFactPattern( final FromCompositeFactPattern pattern,
final boolean isSubPattern ) {
buf.append( indentation );
if ( !isSubPattern && isDSLEnhanced ) {
// adding passthrough markup
buf.append( ">" );
}
if ( pattern.getFactPattern() != null ) {
final LHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
pattern.getFactPattern() );
generateFactPattern( pattern.getFactPattern(),
gctx );
buf.append( " from " );
renderExpression( pattern.getExpression() );
buf.append( "\n" );
}
}
public void visitFromCollectCompositeFactPattern( final FromCollectCompositeFactPattern pattern ) {
visitFromCollectCompositeFactPattern( pattern,
false );
}
public void visitFromCollectCompositeFactPattern( final FromCollectCompositeFactPattern pattern,
final boolean isSubPattern ) {
buf.append( indentation );
if ( !isSubPattern && isDSLEnhanced ) {
// adding passthrough markup
buf.append( ">" );
}
if ( pattern.getFactPattern() != null ) {
final LHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
pattern.getFactPattern() );
generateFactPattern( pattern.getFactPattern(),
gctx );
buf.append( " from collect ( " );
if ( pattern.getRightPattern() != null ) {
if ( pattern.getRightPattern() instanceof FactPattern ) {
generateFactPattern( (FactPattern) pattern.getRightPattern(),
generatorContextFactory.newGeneratorContext() );
} else if ( pattern.getRightPattern() instanceof FromAccumulateCompositeFactPattern ) {
visitFromAccumulateCompositeFactPattern( (FromAccumulateCompositeFactPattern) pattern.getRightPattern(),
isSubPattern );
} else if ( pattern.getRightPattern() instanceof FromCollectCompositeFactPattern ) {
visitFromCollectCompositeFactPattern( (FromCollectCompositeFactPattern) pattern.getRightPattern(),
isSubPattern );
} else if ( pattern.getRightPattern() instanceof FromEntryPointFactPattern ) {
visitFromEntryPointFactPattern( (FromEntryPointFactPattern) pattern.getRightPattern(),
isSubPattern );
} else if ( pattern.getRightPattern() instanceof FromCompositeFactPattern ) {
visitFromCompositeFactPattern( (FromCompositeFactPattern) pattern.getRightPattern(),
isSubPattern );
} else if ( pattern.getRightPattern() instanceof FreeFormLine ) {
visitFreeFormLine( (FreeFormLine) pattern.getRightPattern() );
} else {
throw new IllegalArgumentException( "Unsupported pattern " + pattern.getRightPattern() + " for FROM COLLECT" );
}
}
buf.append( ") \n" );
}
}
public void visitFromAccumulateCompositeFactPattern( final FromAccumulateCompositeFactPattern pattern ) {
visitFromAccumulateCompositeFactPattern( pattern,
false );
}
public void visitFromAccumulateCompositeFactPattern( final FromAccumulateCompositeFactPattern pattern,
final boolean isSubPattern ) {
buf.append( indentation );
if ( !isSubPattern && isDSLEnhanced ) {
// adding passthrough markup
buf.append( ">" );
}
if ( pattern.getFactPattern() != null ) {
final LHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
pattern.getFactPattern() );
generateFactPattern( pattern.getFactPattern(),
gctx );
buf.append( " from accumulate ( " );
if ( pattern.getSourcePattern() != null ) {
if ( pattern.getSourcePattern() instanceof FactPattern ) {
final LHSGeneratorContext soucrceGctx = generatorContextFactory.newGeneratorContext();
generateFactPattern( (FactPattern) pattern.getSourcePattern(),
soucrceGctx );
} else if ( pattern.getSourcePattern() instanceof FromAccumulateCompositeFactPattern ) {
visitFromAccumulateCompositeFactPattern( (FromAccumulateCompositeFactPattern) pattern.getSourcePattern(),
isSubPattern );
} else if ( pattern.getSourcePattern() instanceof FromCollectCompositeFactPattern ) {
visitFromCollectCompositeFactPattern( (FromCollectCompositeFactPattern) pattern.getSourcePattern(),
isSubPattern );
} else if ( pattern.getSourcePattern() instanceof FromEntryPointFactPattern ) {
visitFromEntryPointFactPattern( (FromEntryPointFactPattern) pattern.getSourcePattern(),
isSubPattern );
} else if ( pattern.getSourcePattern() instanceof FromCompositeFactPattern ) {
visitFromCompositeFactPattern( (FromCompositeFactPattern) pattern.getSourcePattern(),
isSubPattern );
} else {
throw new IllegalArgumentException( "Unsupported pattern " + pattern.getSourcePattern() + " for FROM ACCUMULATE" );
}
}
buf.append( ",\n" );
if ( pattern.useFunctionOrCode().equals( FromAccumulateCompositeFactPattern.USE_FUNCTION ) ) {
buf.append( indentation + "\t" );
buf.append( pattern.getFunction() );
} else {
buf.append( indentation + "\tinit( " );
buf.append( pattern.getInitCode() );
buf.append( " ),\n" );
buf.append( indentation + "\taction( " );
buf.append( pattern.getActionCode() );
buf.append( " ),\n" );
if ( pattern.getReverseCode() != null && !pattern.getReverseCode().trim().equals( "" ) ) {
buf.append( indentation + "\treverse( " );
buf.append( pattern.getReverseCode() );
buf.append( " ),\n" );
}
buf.append( indentation + "\tresult( " );
buf.append( pattern.getResultCode() );
buf.append( " )\n" );
}
buf.append( ") \n" );
}
}
public void visitFromEntryPointFactPattern( final FromEntryPointFactPattern pattern ) {
visitFromEntryPointFactPattern( pattern,
false );
}
public void visitFromEntryPointFactPattern( final FromEntryPointFactPattern pattern,
final boolean isSubPattern ) {
buf.append( indentation );
if ( !isSubPattern && isDSLEnhanced ) {
// adding passthrough markup
buf.append( ">" );
}
if ( pattern.getFactPattern() != null ) {
final LHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
pattern.getFactPattern() );
generateFactPattern( pattern.getFactPattern(),
gctx );
buf.append( " from entry-point \"" + pattern.getEntryPointName() + "\"\n" );
}
}
private void renderCompositeFOL( final CompositeFactPattern pattern ) {
buf.append( pattern.getType() );
if ( pattern.getPatterns() != null ) {
buf.append( " (" );
for ( int i = 0; i < pattern.getPatterns().length; i++ ) {
renderSubPattern( pattern,
i );
if ( i != pattern.getPatterns().length - 1 ) {
buf.append( " and " );
}
}
buf.append( ") \n" );
}
}
private void renderSubPattern( final CompositeFactPattern pattern,
final int subIndex ) {
if ( pattern.getPatterns() == null || pattern.getPatterns().length == 0 ) {
return;
}
IFactPattern subPattern = pattern.getPatterns()[ subIndex ];
if ( subPattern instanceof FactPattern ) {
final LHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
subPattern );
this.generateFactPattern( (FactPattern) subPattern,
gctx );
} else if ( subPattern instanceof FromAccumulateCompositeFactPattern ) {
this.visitFromAccumulateCompositeFactPattern( (FromAccumulateCompositeFactPattern) subPattern,
true );
} else if ( subPattern instanceof FromCollectCompositeFactPattern ) {
this.visitFromCollectCompositeFactPattern( (FromCollectCompositeFactPattern) subPattern,
true );
} else if ( subPattern instanceof FromCompositeFactPattern ) {
this.visitFromCompositeFactPattern( (FromCompositeFactPattern) subPattern,
true );
} else {
throw new IllegalStateException( "Unsupported Pattern: " + subPattern.getClass().getName() );
}
}
private void renderExpression( final ExpressionFormLine expression ) {
final ToStringExpressionVisitor visitor = new ToStringExpressionVisitor( expression.getBinding(),
constraintValueBuilder );
buf.append( expression.getText( visitor ) );
}
public void visitDSLSentence( final DSLSentence sentence ) {
buf.append( indentation );
buf.append( sentence.interpolate() );
buf.append( "\n" );
}
private void generateFactPattern( final FactPattern pattern,
final LHSGeneratorContext gctx ) {
if ( pattern.isNegated() ) {
buf.append( "not " );
} else if ( pattern.isBound() ) {
bindingsPatterns.put( pattern.getBoundName(),
pattern );
buf.append( pattern.getBoundName() );
buf.append( " : " );
}
if ( pattern.getFactType() != null ) {
buf.append( pattern.getFactType() );
}
buf.append( "( " );
// top level constraints
if ( pattern.getConstraintList() != null ) {
generateConstraints( pattern,
gctx );
}
buf.append( ")" );
//Add CEP window definition
CEPWindow window = pattern.getWindow();
if ( window.isDefined() ) {
buf.append( " " );
buf.append( window.getOperator() );
buf.append( buildOperatorParameterDRL( window.getParameters() ) );
}
}
private void generateConstraints( final FactPattern pattern,
final LHSGeneratorContext parentContext ) {
LHSGeneratorContext gctx = null;
for ( int constraintIndex = 0; constraintIndex < pattern.getFieldConstraints().length; constraintIndex++ ) {
FieldConstraint constr = pattern.getConstraintList().getConstraints()[ constraintIndex ];
if ( constraintIndex == 0 ) {
gctx = generatorContextFactory.newChildGeneratorContext( parentContext,
constr );
} else {
gctx = generatorContextFactory.newPeerGeneratorContext( gctx,
constr );
}
generateConstraint( constr,
gctx );
}
}
public void generateSeparator( final FieldConstraint constr,
final LHSGeneratorContext gctx ) {
if ( !doesPeerHaveOutput( gctx ) ) {
return;
}
if ( gctx.getParent().getFieldConstraint() instanceof CompositeFieldConstraint ) {
CompositeFieldConstraint cconstr = (CompositeFieldConstraint) gctx.getParent().getFieldConstraint();
buf.append( cconstr.getCompositeJunctionType() + " " );
} else {
if ( buf.length() > 2 && !( buf.charAt( buf.length() - 2 ) == ',' ) ) {
buf.append( ", " );
}
}
}
protected boolean doesPeerHaveOutput( final LHSGeneratorContext gctx ) {
final List<LHSGeneratorContext> peers = generatorContextFactory.getPeers( gctx );
for ( LHSGeneratorContext c : peers ) {
if ( c.isHasOutput() ) {
return true;
}
}
return false;
}
/**
* Recursively process the nested constraints. It will only put brackets
* in for the ones that aren't at top level. This makes for more
* readable DRL in the most common cases.
*/
protected void generateConstraint( final FieldConstraint con,
final LHSGeneratorContext parentContext ) {
generateSeparator( con,
parentContext );
if ( con instanceof CompositeFieldConstraint ) {
CompositeFieldConstraint cfc = (CompositeFieldConstraint) con;
FieldConstraint[] nestedConstraints = cfc.getConstraints();
if ( nestedConstraints != null ) {
LHSGeneratorContext nestedGctx = generatorContextFactory.newChildGeneratorContext( parentContext,
con );
preGenerateNestedConstraint( nestedGctx );
if ( parentContext.getParent().getFieldConstraint() instanceof CompositeFieldConstraint ) {
buf.append( "( " );
}
LHSGeneratorContext gctx = null;
for ( int nestedConstraintIndex = 0; nestedConstraintIndex < nestedConstraints.length; nestedConstraintIndex++ ) {
FieldConstraint nestedConstr = nestedConstraints[ nestedConstraintIndex ];
if ( nestedConstraintIndex == 0 ) {
gctx = generatorContextFactory.newChildGeneratorContext( nestedGctx,
nestedConstr );
} else {
gctx = generatorContextFactory.newPeerGeneratorContext( gctx,
nestedConstr );
}
generateConstraint( nestedConstr,
gctx );
}
if ( parentContext.getParent().getFieldConstraint() instanceof CompositeFieldConstraint ) {
buf.append( ")" );
}
postGenerateNestedConstraint( parentContext );
}
} else {
generateSingleFieldConstraint( (SingleFieldConstraint) con,
parentContext );
}
}
private void generateSingleFieldConstraint( final SingleFieldConstraint constr,
final LHSGeneratorContext gctx ) {
if ( constr.getConstraintValueType() == BaseSingleFieldConstraint.TYPE_PREDICATE ) {
buf.append( "eval( " );
buf.append( constr.getValue() );
buf.append( " )" );
gctx.setHasOutput( true );
} else {
if ( constr.isBound() ) {
bindingsFields.put( constr.getFieldBinding(),
constr );
buf.append( constr.getFieldBinding() );
buf.append( " : " );
}
assertConstraintValue( constr );
if ( isConstraintComplete( constr ) ) {
if ( constr instanceof SingleFieldConstraintEBLeftSide ) {
final SingleFieldConstraintEBLeftSide sfcexp = ( (SingleFieldConstraintEBLeftSide) constr );
final ToStringExpressionVisitor visitor = new ToStringExpressionVisitor( sfcexp.getExpressionLeftSide().getBinding(),
constraintValueBuilder );
buf.append( sfcexp.getExpressionLeftSide().getText( visitor ) );
} else {
SingleFieldConstraint parent = (SingleFieldConstraint) constr.getParent();
StringBuilder parentBuf = new StringBuilder();
while ( parent != null ) {
String fieldName = parent.getFieldName();
parentBuf.insert( 0,
fieldName + "." );
parent = (SingleFieldConstraint) parent.getParent();
}
buf.append( parentBuf );
String fieldName = constr.getFieldName();
buf.append( fieldName );
}
final Map<String, String> parameters = constr.getParameters();
if ( constr.getConnectives() == null ) {
generateNormalFieldRestriction( constr,
parameters );
} else {
generateConnectiveFieldRestriction( constr,
parameters,
gctx );
}
gctx.setHasOutput( true );
}
}
}
private void generateNormalFieldRestriction( final SingleFieldConstraint constr,
final Map<String, String> parameters ) {
if ( constr instanceof SingleFieldConstraintEBLeftSide ) {
SingleFieldConstraintEBLeftSide sfexp = (SingleFieldConstraintEBLeftSide) constr;
addFieldRestriction( buf,
sfexp.getConstraintValueType(),
sfexp.getExpressionLeftSide().getGenericType(),
sfexp.getOperator(),
parameters,
sfexp.getValue(),
sfexp.getExpressionValue(),
true );
} else {
addFieldRestriction( buf,
constr.getConstraintValueType(),
constr.getFieldType(),
constr.getOperator(),
parameters,
constr.getValue(),
constr.getExpressionValue(),
true );
}
}
private void generateConnectiveFieldRestriction( final SingleFieldConstraint constr,
final Map<String, String> parameters,
final LHSGeneratorContext gctx ) {
LHSGeneratorContext cctx = generatorContextFactory.newChildGeneratorContext( gctx,
constr );
if ( constr instanceof SingleFieldConstraintEBLeftSide ) {
SingleFieldConstraintEBLeftSide sfexp = (SingleFieldConstraintEBLeftSide) constr;
addConnectiveFieldRestriction( buf,
sfexp.getConstraintValueType(),
sfexp.getExpressionLeftSide().getGenericType(),
sfexp.getOperator(),
parameters,
sfexp.getValue(),
sfexp.getExpressionValue(),
cctx,
true );
} else {
addConnectiveFieldRestriction( buf,
constr.getConstraintValueType(),
constr.getFieldType(),
constr.getOperator(),
parameters,
constr.getValue(),
constr.getExpressionValue(),
cctx,
true );
}
for ( int j = 0; j < constr.getConnectives().length; j++ ) {
final ConnectiveConstraint conn = constr.getConnectives()[ j ];
final Map<String, String> connectiveParameters = conn.getParameters();
addConnectiveFieldRestriction( buf,
conn.getConstraintValueType(),
conn.getFieldType(),
conn.getOperator(),
connectiveParameters,
conn.getValue(),
conn.getExpressionValue(),
cctx,
true );
}
}
private void assertConstraintValue( final SingleFieldConstraint sfc ) {
if ( DataType.TYPE_STRING.equals( sfc.getFieldType() ) ) {
if ( sfc.getValue() == null ) {
sfc.setValue( "" );
}
}
}
private boolean isConstraintComplete( final SingleFieldConstraint constr ) {
if ( constr.getConstraintValueType() == BaseSingleFieldConstraint.TYPE_EXPR_BUILDER_VALUE ) {
return true;
} else if ( constr instanceof SingleFieldConstraintEBLeftSide ) {
return true;
} else if ( constr.getFieldBinding() != null ) {
return true;
}
final String operator = constr.getOperator();
final String fieldType = constr.getFieldType();
final String fieldValue = constr.getValue();
if ( operator == null ) {
return false;
}
if ( operator.equals( "== null" ) || operator.equals( "!= null" ) ) {
return true;
}
if ( DataType.TYPE_STRING.equals( fieldType ) ) {
return true;
}
return !( fieldValue == null || fieldValue.isEmpty() );
}
protected void addConnectiveFieldRestriction( final StringBuilder buf,
final int type,
final String fieldType,
final String operator,
final Map<String, String> parameters,
final String value,
final ExpressionFormLine expression,
LHSGeneratorContext gctx,
final boolean spaceBeforeOperator ) {
addFieldRestriction( buf, type, fieldType, operator, parameters, value, expression, spaceBeforeOperator );
}
private void addFieldRestriction( final StringBuilder buf,
final int type,
final String fieldType,
final String operator,
final Map<String, String> parameters,
final String value,
final ExpressionFormLine expression,
final boolean spaceBeforeOperator ) {
if ( operator == null ) {
return;
}
if ( spaceBeforeOperator ) {
buf.append( " " );
}
buf.append( operator );
if ( parameters != null && parameters.size() > 0 ) {
buf.append( buildOperatorParameterDRL( parameters ) );
}
switch ( type ) {
case BaseSingleFieldConstraint.TYPE_RET_VALUE:
buildReturnValueFieldValue( value,
buf );
break;
case BaseSingleFieldConstraint.TYPE_LITERAL:
buildLiteralFieldValue( operator,
type,
fieldType,
value,
buf );
break;
case BaseSingleFieldConstraint.TYPE_EXPR_BUILDER_VALUE:
buildExpressionFieldValue( expression,
buf );
break;
case BaseSingleFieldConstraint.TYPE_TEMPLATE:
buildTemplateFieldValue( type,
fieldType,
value,
buf );
break;
case BaseSingleFieldConstraint.TYPE_ENUM:
buildEnumFieldValue( operator,
type,
fieldType,
value,
buf );
break;
default:
buildDefaultFieldValue( operator,
value,
buf );
}
}
protected void buildReturnValueFieldValue( final String value,
final StringBuilder buf ) {
buf.append( " " );
buf.append( "( " );
buf.append( value );
buf.append( " )" );
buf.append( " " );
}
protected StringBuilder buildOperatorParameterDRL( final Map<String, String> parameters ) {
String className = parameters.get( SharedConstants.OPERATOR_PARAMETER_GENERATOR );
if ( className == null ) {
throw new IllegalStateException( "Implementation of 'org.kie.guvnor.guided.server.util.OperatorParameterDRLBuilder' undefined. Unable to build Operator Parameter DRL." );
}
try {
OperatorParameterDRLBuilder builder = (OperatorParameterDRLBuilder) Class.forName( className ).newInstance();
return builder.buildDRL( parameters );
} catch ( ClassNotFoundException cnfe ) {
throw new IllegalStateException( "Unable to generate Operator DRL using class '" + className + "'.",
cnfe );
} catch ( IllegalAccessException iae ) {
throw new IllegalStateException( "Unable to generate Operator DRL using class '" + className + "'.",
iae );
} catch ( InstantiationException ie ) {
throw new IllegalStateException( "Unable to generate Operator DRL using class '" + className + "'.",
ie );
}
}
protected void buildLiteralFieldValue( final String operator,
final int type,
final String fieldType,
final String value,
final StringBuilder buf ) {
if ( OperatorsOracle.operatorRequiresList( operator ) ) {
populateValueList( buf,
type,
fieldType,
value );
} else {
if ( !operator.equals( "== null" ) && !operator.equals( "!= null" ) ) {
buf.append( " " );
constraintValueBuilder.buildLHSFieldValue( buf,
type,
fieldType,
value );
}
}
buf.append( " " );
}
private void populateValueList( final StringBuilder buf,
final int type,
final String fieldType,
final String value ) {
String workingValue = value.trim();
if ( workingValue.startsWith( "(" ) ) {
workingValue = workingValue.substring( 1 );
}
if ( workingValue.endsWith( ")" ) ) {
workingValue = workingValue.substring( 0,
workingValue.length() - 1 );
}
final String[] values = workingValue.split( "," );
buf.append( " ( " );
for ( String v : values ) {
v = v.trim();
if ( v.startsWith( "\"" ) ) {
v = v.substring( 1 );
}
if ( v.endsWith( "\"" ) ) {
v = v.substring( 0,
v.length() - 1 );
}
constraintValueBuilder.buildLHSFieldValue( buf,
type,
fieldType,
v );
buf.append( ", " );
}
buf.delete( buf.length() - 2,
buf.length() );
buf.append( " )" );
}
protected void buildExpressionFieldValue( final ExpressionFormLine expression,
final StringBuilder buf ) {
if ( expression != null ) {
buf.append( " " );
final ToStringExpressionVisitor visitor = new ToStringExpressionVisitor( expression.getBinding(),
constraintValueBuilder );
buf.append( expression.getText( visitor ) );
buf.append( " " );
}
}
protected void buildTemplateFieldValue( final int type,
final String fieldType,
final String value,
final StringBuilder buf ) {
buf.append( " " );
constraintValueBuilder.buildLHSFieldValue( buf,
type,
fieldType,
"@{removeDelimitingQuotes(" + value + ")}" );
buf.append( " " );
}
private void buildEnumFieldValue( final String operator,
final int type,
final String fieldType,
final String value,
final StringBuilder buf ) {
if ( OperatorsOracle.operatorRequiresList( operator ) ) {
populateValueList( buf,
type,
fieldType,
value );
} else {
if ( !operator.equals( "== null" ) && !operator.equals( "!= null" ) ) {
buf.append( " " );
constraintValueBuilder.buildLHSFieldValue( buf,
type,
fieldType,
value );
}
}
buf.append( " " );
}
protected void buildDefaultFieldValue( final String operator,
final String value,
final StringBuilder buf ) {
if ( !operator.equals( "== null" ) && !operator.equals( "!= null" ) ) {
buf.append( " " );
buf.append( value );
}
buf.append( " " );
}
}
public static class RHSActionVisitor extends ReflectiveVisitor {
protected StringBuilder buf;
private boolean isDSLEnhanced;
private String indentation;
private Map<String, IFactPattern> bindingsPatterns;
private Map<String, FieldConstraint> bindingsFields;
protected DRLConstraintValueBuilder constraintValueBuilder;
protected RHSGeneratorContextFactory generatorContextFactory;
protected final RHSGeneratorContext rootContext;
//Keep a record of Work Items that are instantiated for Actions that depend on them
private Set<String> instantiatedWorkItems;
public RHSActionVisitor( final boolean isDSLEnhanced,
final Map<String, IFactPattern> bindingsPatterns,
final Map<String, FieldConstraint> bindingsFields,
final DRLConstraintValueBuilder constraintValueBuilder,
final RHSGeneratorContextFactory generatorContextFactory,
final StringBuilder b,
final String indentation ) {
this.isDSLEnhanced = isDSLEnhanced;
this.bindingsPatterns = bindingsPatterns;
this.bindingsFields = bindingsFields;
this.constraintValueBuilder = constraintValueBuilder;
this.generatorContextFactory = generatorContextFactory;
this.rootContext = generatorContextFactory.newGeneratorContext();
this.indentation = indentation;
this.instantiatedWorkItems = new HashSet<String>();
this.buf = b;
}
protected void preGenerateAction( final RHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void postGenerateAction( final RHSGeneratorContext gctx ) {
// empty, overridden by rule templates
}
protected void preGenerateSetMethodCallParameterValue( final RHSGeneratorContext gctx,
final ActionFieldValue fieldValue ) {
gctx.setHasOutput( true );
}
public void visitActionInsertFact( final ActionInsertFact action ) {
this.generateInsertCall( action,
false );
}
public void visitActionInsertLogicalFact( final ActionInsertLogicalFact action ) {
this.generateInsertCall( action,
true );
}
public void visitFreeFormLine( final FreeFormLine ffl ) {
if ( ffl.getText() == null ) {
return;
}
String[] lines = ffl.getText().split( "\\n|\\r\\n" );
for ( String line : lines ) {
this.buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
this.buf.append( line + "\n" );
}
}
private void generateInsertCall( final ActionInsertFact action,
final boolean isLogic ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
final String binding = action.getBoundName();
if ( action.getFieldValues().length == 0 && binding == null ) {
buf.append( ( isLogic ) ? "insertLogical( new " : "insert( new " );
buf.append( action.getFactType() );
buf.append( "() );\n" );
} else {
buf.append( action.getFactType() );
buf.append( " " + binding );
buf.append( " = new " );
buf.append( action.getFactType() );
buf.append( "();\n" );
generateSetMethodCalls( binding,
action.getFieldValues() );
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
if ( isLogic ) {
buf.append( "insertLogical( " );
buf.append( binding );
buf.append( " );\n" );
} else {
buf.append( "insert( " );
buf.append( binding );
buf.append( " );\n" );
}
}
}
public void visitActionUpdateField( final ActionUpdateField action ) {
final RHSGeneratorContext gctx = generatorContextFactory.newChildGeneratorContext( rootContext,
action );
preGenerateAction( gctx );
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
buf.append( "modify( " ).append( action.getVariable() ).append( " ) {\n" );
this.generateModifyMethodCalls( action.getFieldValues(),
gctx );
buf.append( "\n" ).append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
buf.append( "}\n" );
postGenerateAction( gctx );
}
public void visitActionGlobalCollectionAdd( final ActionGlobalCollectionAdd add ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
buf.append( add.getGlobalName() + ".add( " + add.getFactName() + " );\n" );
}
public void visitActionRetractFact( final ActionRetractFact action ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
buf.append( "retract( " );
buf.append( action.getVariableName() );
buf.append( " );\n" );
}
public void visitDSLSentence( final DSLSentence sentence ) {
buf.append( indentation );
buf.append( sentence.interpolate() );
buf.append( "\n" );
}
public void visitActionExecuteWorkItem( final ActionExecuteWorkItem action ) {
String wiName = action.getWorkDefinition().getName();
String wiImplName = WORKITEM_PREFIX + wiName;
instantiatedWorkItems.add( wiName );
buf.append( indentation );
buf.append( "org.drools.core.process.instance.impl.WorkItemImpl " );
buf.append( wiImplName );
buf.append( " = new org.drools.core.process.instance.impl.WorkItemImpl();\n" );
buf.append( indentation );
buf.append( wiImplName );
buf.append( ".setName( \"" );
buf.append( wiName );
buf.append( "\" );\n" );
for ( PortableParameterDefinition ppd : action.getWorkDefinition().getParameters() ) {
makeWorkItemParameterDRL( ppd,
wiImplName );
}
buf.append( indentation );
buf.append( "wim.internalExecuteWorkItem( " );
buf.append( wiImplName );
buf.append( " );\n" );
}
private void makeWorkItemParameterDRL( final PortableParameterDefinition ppd,
final String wiImplName ) {
boolean makeParameter = true;
//Only add bound parameters if their binding exists (i.e. the corresponding column has a value or - for Limited Entry - is true)
if ( ppd instanceof HasBinding ) {
HasBinding hb = (HasBinding) ppd;
if ( hb.isBound() ) {
String binding = hb.getBinding();
makeParameter = isBindingValid( binding );
}
}
if ( makeParameter ) {
buf.append( indentation );
buf.append( wiImplName );
buf.append( ".getParameters().put( \"" );
buf.append( ppd.getName() );
buf.append( "\", " );
buf.append( ppd.asString() );
buf.append( " );\n" );
}
}
private boolean isBindingValid( final String binding ) {
if ( bindingsPatterns.containsKey( binding ) ) {
return true;
}
if ( bindingsFields.containsKey( binding ) ) {
return true;
}
return false;
}
public void visitActionSetField( final ActionSetField action ) {
if ( action instanceof ActionCallMethod ) {
this.generateSetMethodCallsMethod( (ActionCallMethod) action,
action.getFieldValues() );
} else {
this.generateSetMethodCalls( action.getVariable(),
action.getFieldValues() );
}
}
private void generateSetMethodCalls( final String variableName,
final ActionFieldValue[] fieldValues ) {
for ( int i = 0; i < fieldValues.length; i++ ) {
generateSetMethodCall( variableName,
fieldValues[ i ] );
}
}
protected void generateSetMethodCall( final String variableName,
final ActionFieldValue fieldValue ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
buf.append( variableName );
if ( fieldValue instanceof ActionFieldFunction ) {
buf.append( "." );
buf.append( fieldValue.getField() );
} else {
buf.append( ".set" );
buf.append( Character.toUpperCase( fieldValue.getField().charAt( 0 ) ) );
buf.append( fieldValue.getField().substring( 1 ) );
}
buf.append( "( " );
generateSetMethodCallParameterValue( buf,
fieldValue );
buf.append( " );\n" );
}
private void generateSetMethodCallParameterValue( final StringBuilder buf,
final ActionFieldValue fieldValue ) {
if ( fieldValue.isFormula() ) {
buildFormulaFieldValue( fieldValue,
buf );
} else if ( fieldValue.getNature() == FieldNatureType.TYPE_VARIABLE ) {
buildVariableFieldValue( fieldValue,
buf );
} else if ( fieldValue.getNature() == FieldNatureType.TYPE_TEMPLATE ) {
buildTemplateFieldValue( fieldValue,
buf );
} else if ( fieldValue instanceof ActionWorkItemFieldValue ) {
buildWorkItemFieldValue( (ActionWorkItemFieldValue) fieldValue,
buf );
} else {
buildDefaultFieldValue( fieldValue,
buf );
}
}
private void generateModifyMethodCalls( final ActionFieldValue[] fieldValues,
final RHSGeneratorContext parentContext ) {
RHSGeneratorContext gctx = null;
for ( int index = 0; index < fieldValues.length; index++ ) {
final ActionFieldValue fieldValue = fieldValues[ index ];
if ( index == 0 ) {
gctx = generatorContextFactory.newChildGeneratorContext( parentContext,
fieldValue );
} else {
gctx = generatorContextFactory.newPeerGeneratorContext( gctx,
fieldValue );
}
preGenerateSetMethodCallParameterValue( gctx,
fieldValue );
generateModifyMethodSeparator( gctx,
fieldValue );
generateModifyMethodCall( gctx,
fieldValue );
}
}
protected void generateModifyMethodCall( final RHSGeneratorContext gctx,
final ActionFieldValue fieldValue ) {
buf.append( indentation ).append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
if ( fieldValue instanceof ActionFieldFunction ) {
buf.append( fieldValue.getField() );
} else {
buf.append( "set" );
buf.append( Character.toUpperCase( fieldValue.getField().charAt( 0 ) ) );
buf.append( fieldValue.getField().substring( 1 ) );
}
buf.append( "( " );
generateSetMethodCallParameterValue( buf,
fieldValue );
buf.append( " )" );
}
protected void generateModifyMethodSeparator( final RHSGeneratorContext gctx,
final ActionFieldValue fieldValue ) {
if ( doesPeerHaveOutput( gctx ) ) {
buf.append( ", \n" );
}
}
private boolean doesPeerHaveOutput( final RHSGeneratorContext gctx ) {
final List<RHSGeneratorContext> peers = generatorContextFactory.getPeers( gctx );
for ( RHSGeneratorContext c : peers ) {
if ( c.isHasOutput() ) {
return true;
}
}
return false;
}
protected void buildFormulaFieldValue( final ActionFieldValue fieldValue,
final StringBuilder buf ) {
buf.append( fieldValue.getValue() );
}
protected void buildVariableFieldValue( final ActionFieldValue fieldValue,
final StringBuilder buf ) {
buf.append( fieldValue.getValue().substring( 1 ) );
}
protected void buildTemplateFieldValue( final ActionFieldValue fieldValue,
final StringBuilder buf ) {
constraintValueBuilder.buildRHSFieldValue( buf,
fieldValue.getType(),
"@{removeDelimitingQuotes(" + fieldValue.getValue() + ")}" );
}
protected void buildWorkItemFieldValue( final ActionWorkItemFieldValue afv,
final StringBuilder buf ) {
if ( instantiatedWorkItems.contains( afv.getWorkItemName() ) ) {
buf.append( "(" );
buf.append( afv.getWorkItemParameterClassName() );
buf.append( ") " );
buf.append( WORKITEM_PREFIX );
buf.append( afv.getWorkItemName() );
buf.append( ".getResult( \"" );
buf.append( afv.getWorkItemParameterName() );
buf.append( "\" )" );
} else {
buf.append( "null" );
}
}
protected void buildDefaultFieldValue( final ActionFieldValue fieldValue,
final StringBuilder buf ) {
constraintValueBuilder.buildRHSFieldValue( buf,
fieldValue.getType(),
fieldValue.getValue() );
}
private void generateSetMethodCallsMethod( final ActionCallMethod action,
final FieldNature[] fieldValues ) {
buf.append( indentation );
if ( isDSLEnhanced ) {
buf.append( ">" );
}
buf.append( action.getVariable() );
buf.append( "." );
buf.append( action.getMethodName() );
buf.append( "( " );
boolean isFirst = true;
for ( int i = 0; i < fieldValues.length; i++ ) {
ActionFieldFunction valueFunction = (ActionFieldFunction) fieldValues[ i ];
if ( isFirst == true ) {
isFirst = false;
} else {
buf.append( ", " );
}
if ( valueFunction.isFormula() ) {
buf.append( valueFunction.getValue() );
} else if ( valueFunction.getNature() == FieldNatureType.TYPE_VARIABLE ) {
buf.append( valueFunction.getValue() );
} else {
buildDefaultFieldValue( valueFunction,
buf );
}
}
buf.append( " );\n" );
}
}
public static class RHSClassDependencyVisitor extends ReflectiveVisitor {
private Map<String, List<ActionFieldValue>> classes = new HashMap<String, List<ActionFieldValue>>();
public void visitFreeFormLine( FreeFormLine ffl ) {
//Do nothing other than preventing ReflectiveVisitor recording an error
}
public void visitActionGlobalCollectionAdd( final ActionGlobalCollectionAdd add ) {
//Do nothing other than preventing ReflectiveVisitor recording an error
}
public void visitActionRetractFact( final ActionRetractFact action ) {
//Do nothing other than preventing ReflectiveVisitor recording an error
}
public void visitDSLSentence( final DSLSentence sentence ) {
//Do nothing other than preventing ReflectiveVisitor recording an error
}
public void visitActionInsertFact( final ActionInsertFact action ) {
getClasses( action.getFieldValues() );
}
public void visitActionInsertLogicalFact( final ActionInsertLogicalFact action ) {
getClasses( action.getFieldValues() );
}
public void visitActionUpdateField( final ActionUpdateField action ) {
getClasses( action.getFieldValues() );
}
public void visitActionSetField( final ActionSetField action ) {
getClasses( action.getFieldValues() );
}
public void visitActionExecuteWorkItem( final ActionExecuteWorkItem action ) {
//Do nothing other than preventing ReflectiveVisitor recording an error
}
public Map<String, List<ActionFieldValue>> getRHSClasses() {
return classes;
}
private void getClasses( ActionFieldValue[] fieldValues ) {
for ( ActionFieldValue afv : fieldValues ) {
String type = afv.getType();
List<ActionFieldValue> afvs = classes.get( type );
if ( afvs == null ) {
afvs = new ArrayList<ActionFieldValue>();
classes.put( type,
afvs );
}
afvs.add( afv );
}
}
}
/**
* @see RuleModelPersistence#unmarshal(String, List, PackageDataModelOracle)
*/
public RuleModel unmarshal( final String str,
final List<String> globals,
final PackageDataModelOracle dmo ) {
if ( str == null || str.isEmpty() ) {
return new RuleModel();
}
return getRuleModel( preprocessDRL( str,
false ).registerGlobals( dmo,
globals ),
dmo );
}
public RuleModel unmarshalUsingDSL( final String str,
final List<String> globals,
final PackageDataModelOracle dmo,
final String... dsls ) {
if ( str == null || str.isEmpty() ) {
return new RuleModel();
}
return getRuleModel( parseDSLs( preprocessDRL( str,
true ),
dsls ).registerGlobals( dmo,
globals ),
dmo );
}
private ExpandedDRLInfo parseDSLs( final ExpandedDRLInfo expandedDRLInfo,
final String[] dsls ) {
for ( String dsl : dsls ) {
for ( String line : dsl.split( "\n" ) ) {
String dslPattern = line.trim();
if ( dslPattern.length() > 0 ) {
if ( dslPattern.startsWith( "[when]" ) ) {
expandedDRLInfo.lhsDslPatterns.add( extractDslPattern( dslPattern.substring( "[when]".length() ) ) );
} else if ( dslPattern.startsWith( "[then]" ) ) {
expandedDRLInfo.rhsDslPatterns.add( extractDslPattern( dslPattern.substring( "[then]".length() ) ) );
} else if ( dslPattern.startsWith( "[" ) ) {
String pattern = extractDslPattern( removeDslTopics( dslPattern ) );
expandedDRLInfo.lhsDslPatterns.add( pattern );
expandedDRLInfo.rhsDslPatterns.add( pattern );
}
}
}
}
return expandedDRLInfo;
}
private String removeDslTopics( final String line ) {
int lastClosedSquare = -1;
boolean lookForOpen = true;
for ( int i = 0; i < line.length(); i++ ) {
char ch = line.charAt( i );
if ( lookForOpen ) {
if ( ch == '[' ) {
lookForOpen = false;
} else if ( !Character.isWhitespace( ch ) ) {
break;
}
} else {
if ( ch == ']' ) {
lastClosedSquare = i;
lookForOpen = true;
}
}
}
return line.substring( lastClosedSquare + 1 );
}
private String extractDslPattern( final String line ) {
return line.substring( 0,
line.indexOf( '=' ) ).trim();
}
private RuleModel getRuleModel( final ExpandedDRLInfo expandedDRLInfo,
final PackageDataModelOracle dmo ) {
//De-serialize model
RuleDescr ruleDescr = parseDrl( expandedDRLInfo );
RuleModel model = new RuleModel();
model.name = ruleDescr.getName();
model.parentName = ruleDescr.getParentName();
Map<String, AnnotationDescr> annotations = ruleDescr.getAnnotations();
if ( annotations != null ) {
for ( AnnotationDescr annotation : annotations.values() ) {
model.addMetadata( new RuleMetadata( annotation.getName(),
annotation.getValuesAsString() ) );
}
}
//De-serialize Package name
final String packageName = PackageNameParser.parsePackageName( expandedDRLInfo.plainDrl );
model.setPackageName( packageName );
//De-serialize imports
final Imports imports = ImportsParser.parseImports( expandedDRLInfo.plainDrl );
for ( Import item : imports.getImports() ) {
model.getImports().addImport( item );
}
boolean isJavaDialect = parseAttributes( model,
ruleDescr.getAttributes() );
Map<String, String> boundParams = parseLhs( model,
ruleDescr.getLhs(),
isJavaDialect,
expandedDRLInfo,
dmo );
parseRhs( model,
expandedDRLInfo.consequence != null ? expandedDRLInfo.consequence : (String) ruleDescr.getConsequence(),
isJavaDialect,
boundParams,
expandedDRLInfo,
dmo );
return model;
}
private ExpandedDRLInfo preprocessDRL( final String str,
final boolean hasDsl ) {
StringBuilder drl = new StringBuilder();
String thenLine = null;
List<String> lhsStatements = new ArrayList<String>();
List<String> rhsStatements = new ArrayList<String>();
String[] lines = str.split( "\n" );
RuleSection ruleSection = RuleSection.HEADER;
int lhsParenthesisBalance = 0;
for ( String line : lines ) {
if ( ruleSection == RuleSection.HEADER ) {
drl.append( line ).append( "\n" );
if ( line.contains( "when" ) ) {
ruleSection = RuleSection.LHS;
}
continue;
}
if ( ruleSection == RuleSection.LHS && line.contains( "then" ) ) {
thenLine = line;
ruleSection = RuleSection.RHS;
continue;
}
if ( ruleSection == RuleSection.LHS ) {
if ( lhsParenthesisBalance == 0 ) {
lhsStatements.add( line );
} else {
String oldLine = lhsStatements.remove( lhsStatements.size() - 1 );
lhsStatements.add( oldLine + " " + line );
}
lhsParenthesisBalance += parenthesisBalance( line );
} else {
rhsStatements.add( line );
}
}
return createExpandedDRLInfo( hasDsl,
drl,
thenLine,
lhsStatements,
rhsStatements );
}
private int parenthesisBalance( String str ) {
int balance = 0;
for ( char ch : str.toCharArray() ) {
if ( ch == '(' ) {
balance++;
} else if ( ch == ')' ) {
balance--;
}
}
return balance;
}
private ExpandedDRLInfo createExpandedDRLInfo( final boolean hasDsl,
final StringBuilder drl,
final String thenLine,
final List<String> lhsStatements,
final List<String> rhsStatements ) {
if ( !hasDsl ) {
return processFreeFormStatement( drl,
thenLine,
lhsStatements,
rhsStatements );
}
ExpandedDRLInfo expandedDRLInfo = new ExpandedDRLInfo( hasDsl );
int lineCounter = -1;
for ( String statement : lhsStatements ) {
lineCounter++;
String trimmed = statement.trim();
if ( hasDsl && trimmed.startsWith( ">" ) ) {
if ( isValidLHSStatement( trimmed ) ) {
drl.append( trimmed.substring( 1 ) ).append( "\n" );
} else {
expandedDRLInfo.freeFormStatementsInLhs.put( lineCounter,
trimmed.substring( 1 ) );
}
} else {
expandedDRLInfo.dslStatementsInLhs.put( lineCounter,
trimmed );
}
}
drl.append( thenLine ).append( "\n" );
lineCounter = -1;
for ( String statement : rhsStatements ) {
lineCounter++;
String trimmed = statement.trim();
if ( trimmed.endsWith( "end" ) ) {
trimmed = trimmed.substring( 0,
trimmed.length() - 3 ).trim();
}
if ( trimmed.length() > 0 ) {
if ( hasDsl && trimmed.startsWith( ">" ) ) {
drl.append( trimmed.substring( 1 ) ).append( "\n" );
} else {
expandedDRLInfo.dslStatementsInRhs.put( lineCounter,
trimmed );
}
}
}
expandedDRLInfo.plainDrl = drl.toString();
return expandedDRLInfo;
}
private ExpandedDRLInfo processFreeFormStatement( final StringBuilder drl,
final String thenLine,
final List<String> lhsStatements,
final List<String> rhsStatements ) {
ExpandedDRLInfo expandedDRLInfo = new ExpandedDRLInfo( false );
int lineCounter = -1;
for ( String statement : lhsStatements ) {
lineCounter++;
String trimmed = statement.trim();
if ( isValidLHSStatement( trimmed ) ) {
drl.append( trimmed ).append( "\n" );
} else {
expandedDRLInfo.freeFormStatementsInLhs.put( lineCounter,
trimmed );
}
}
drl.append( thenLine ).append( "\n" );
expandedDRLInfo.consequence = "";
lineCounter = -1;
for ( String statement : rhsStatements ) {
String trimmed = statement.trim();
if ( trimmed.endsWith( "end" ) ) {
trimmed = trimmed.substring( 0, trimmed.length() - 3 );
}
if ( trimmed.length() > 0 ) {
expandedDRLInfo.consequence += ( trimmed + "\n" );
}
drl.append( statement ).append( "\n" );
}
expandedDRLInfo.plainDrl = drl.toString();
return expandedDRLInfo;
}
private boolean isValidLHSStatement( final String lhs ) {
// TODO: How to identify a non valid (free form) lhs statement?
return ( lhs.indexOf( '(' ) >= 0 || lhs.indexOf( ':' ) >= 0 ) && lhs.indexOf( "//" ) == -1;
}
private enum RuleSection {HEADER, LHS, RHS}
private static class ExpandedDRLInfo {
private final boolean hasDsl;
private String plainDrl;
private String consequence;
private Map<Integer, String> dslStatementsInLhs;
private Map<Integer, String> dslStatementsInRhs;
private Map<Integer, String> freeFormStatementsInLhs;
private List<String> lhsDslPatterns;
private List<String> rhsDslPatterns;
private Set<String> globals = new HashSet<String>();
private ExpandedDRLInfo( final boolean hasDsl ) {
this.hasDsl = hasDsl;
dslStatementsInLhs = new HashMap<Integer, String>();
dslStatementsInRhs = new HashMap<Integer, String>();
freeFormStatementsInLhs = new HashMap<Integer, String>();
lhsDslPatterns = new ArrayList<String>();
rhsDslPatterns = new ArrayList<String>();
}
public boolean hasGlobal( final String name ) {
return globals.contains( name );
}
public ExpandedDRLInfo registerGlobals( final PackageDataModelOracle dmo,
final List<String> globalStatements ) {
if ( globalStatements != null ) {
for ( String globalStatement : globalStatements ) {
String identifier = getIdentifier( globalStatement );
if ( identifier != null ) {
globals.add( identifier );
}
}
}
Map<String, String> globalsFromDmo = dmo != null ? dmo.getPackageGlobals() : null;
if ( globalsFromDmo != null ) {
globals.addAll( globalsFromDmo.keySet() );
}
return this;
}
private String getIdentifier( String globalStatement ) {
globalStatement = globalStatement.trim();
if ( !globalStatement.startsWith( "global" ) ) {
return null;
}
int lastSpace = globalStatement.lastIndexOf( ' ' );
if ( lastSpace < 0 ) {
return null;
}
String identifier = globalStatement.substring( lastSpace + 1 );
if ( identifier.endsWith( ";" ) ) {
identifier = identifier.substring( 0,
identifier.length() - 1 );
}
return identifier;
}
public ExpandedDRLInfo registerGlobalDescrs( final List<GlobalDescr> globalDescrs ) {
if ( globalDescrs != null ) {
for ( GlobalDescr globalDescr : globalDescrs ) {
globals.add( globalDescr.getIdentifier() );
}
}
return this;
}
}
private RuleDescr parseDrl( final ExpandedDRLInfo expandedDRLInfo ) {
DrlParser drlParser = new DrlParser();
PackageDescr packageDescr;
try {
packageDescr = drlParser.parse( true, expandedDRLInfo.plainDrl );
} catch ( DroolsParserException e ) {
throw new RuntimeException( e );
}
expandedDRLInfo.registerGlobalDescrs( packageDescr.getGlobals() );
return packageDescr.getRules().get( 0 );
}
private boolean parseAttributes( final RuleModel m,
final Map<String, AttributeDescr> attributes ) {
boolean isJavaDialect = false;
for ( Map.Entry<String, AttributeDescr> entry : attributes.entrySet() ) {
String name = entry.getKey();
String value = normalizeAttributeValue( entry.getValue().getValue().trim() );
RuleAttribute ruleAttribute = new RuleAttribute( name,
value );
m.addAttribute( ruleAttribute );
isJavaDialect |= name.equals( "dialect" ) && value.equals( "java" );
}
return isJavaDialect;
}
private String normalizeAttributeValue( String value ) {
if ( value.startsWith( "[" ) ) {
value = value.substring( 1, value.length() - 1 ).trim();
}
if ( value.startsWith( "\"" ) ) {
StringBuilder sb = new StringBuilder();
String[] split = value.split( "," );
sb.append( stripQuotes( split[ 0 ].trim() ) );
for ( int i = 1; i < split.length; i++ ) {
sb.append( ", " ).append( stripQuotes( split[ i ].trim() ) );
}
value = sb.toString();
}
return value;
}
private String stripQuotes( String value ) {
if ( value.startsWith( "\"" ) ) {
value = value.substring( 1, value.length() - 1 ).trim();
}
return value;
}
private Map<String, String> parseLhs( final RuleModel m,
final AndDescr lhs,
final boolean isJavaDialect,
final ExpandedDRLInfo expandedDRLInfo,
final PackageDataModelOracle dmo ) {
Map<String, String> boundParams = new HashMap<String, String>();
int lineCounter = -1;
for ( BaseDescr descr : lhs.getDescrs() ) {
lineCounter = parseNonDrlInLhs( m,
expandedDRLInfo,
lineCounter );
IPattern pattern = parseBaseDescr( m,
descr,
isJavaDialect,
boundParams,
dmo );
if ( pattern != null ) {
m.addLhsItem( pattern );
}
}
parseNonDrlInLhs( m,
expandedDRLInfo,
lineCounter );
return boundParams;
}
private int parseNonDrlInLhs( final RuleModel m,
final ExpandedDRLInfo expandedDRLInfo,
int lineCounter ) {
lineCounter++;
lineCounter = parseDslInLhs( m,
expandedDRLInfo,
lineCounter );
lineCounter = parseFreeForm( m,
expandedDRLInfo,
lineCounter );
return lineCounter;
}
private int parseDslInLhs( final RuleModel m,
final ExpandedDRLInfo expandedDRLInfo,
int lineCounter ) {
if ( expandedDRLInfo.hasDsl ) {
String dslLine = expandedDRLInfo.dslStatementsInLhs.get( lineCounter );
while ( dslLine != null ) {
m.addLhsItem( toDSLSentence( expandedDRLInfo.lhsDslPatterns,
dslLine ) );
dslLine = expandedDRLInfo.dslStatementsInLhs.get( ++lineCounter );
}
}
return lineCounter;
}
private int parseFreeForm( final RuleModel m,
final ExpandedDRLInfo expandedDRLInfo,
int lineCounter ) {
String freeForm = expandedDRLInfo.freeFormStatementsInLhs.get( lineCounter );
while ( freeForm != null ) {
FreeFormLine ffl = new FreeFormLine();
ffl.setText( freeForm );
m.addLhsItem( ffl );
freeForm = expandedDRLInfo.freeFormStatementsInLhs.get( ++lineCounter );
}
return lineCounter;
}
private IPattern parseBaseDescr( final RuleModel m,
final BaseDescr descr,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
if ( descr instanceof PatternDescr ) {
return parsePatternDescr( m,
(PatternDescr) descr,
isJavaDialect,
boundParams,
dmo );
} else if ( descr instanceof AndDescr ) {
AndDescr andDescr = (AndDescr) descr;
return parseBaseDescr( m,
andDescr.getDescrs().get( 0 ),
isJavaDialect,
boundParams,
dmo );
} else if ( descr instanceof EvalDescr ) {
FreeFormLine freeFormLine = new FreeFormLine();
freeFormLine.setText( "eval( " + ( (EvalDescr) descr ).getContent() + " )" );
return freeFormLine;
} else if ( descr instanceof ConditionalElementDescr ) {
return parseExistentialElementDescr( m,
(ConditionalElementDescr) descr,
isJavaDialect,
boundParams,
dmo );
}
return null;
}
private IFactPattern parsePatternDescr( final RuleModel m,
final PatternDescr pattern,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
if ( pattern.getSource() != null ) {
return parsePatternSource( m,
pattern,
pattern.getSource(),
isJavaDialect,
boundParams,
dmo );
}
return getFactPattern( m,
pattern,
isJavaDialect,
boundParams,
dmo );
}
private FactPattern getFactPattern( final RuleModel m,
final PatternDescr pattern,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
String type = pattern.getObjectType();
FactPattern factPattern = new FactPattern( getSimpleFactType( type,
dmo ) );
if ( pattern.getIdentifier() != null ) {
String identifier = pattern.getIdentifier();
factPattern.setBoundName( identifier );
boundParams.put( identifier,
type );
}
parseConstraint( m,
factPattern,
pattern.getConstraint(),
isJavaDialect,
boundParams,
dmo );
for ( BehaviorDescr behavior : pattern.getBehaviors() ) {
if ( behavior.getText().equals( "window" ) ) {
CEPWindow window = new CEPWindow();
window.setOperator( "over window:" + behavior.getSubType() );
window.setParameter( "org.drools.workbench.models.commons.backend.rule.operatorParameterGenerator",
"org.drools.workbench.models.commons.backend.rule.CEPWindowOperatorParameterDRLBuilder" );
List<String> params = behavior.getParameters();
if ( params != null ) {
int i = 1;
for ( String param : params ) {
window.setParameter( "" + i++, param );
}
}
factPattern.setWindow( window );
}
}
return factPattern;
}
private IFactPattern parsePatternSource( final RuleModel m,
final PatternDescr pattern,
final PatternSourceDescr patternSource,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
if ( patternSource instanceof AccumulateDescr ) {
AccumulateDescr accumulate = (AccumulateDescr) patternSource;
FromAccumulateCompositeFactPattern fac = new FromAccumulateCompositeFactPattern();
fac.setSourcePattern( parseBaseDescr( m,
accumulate.getInput(),
isJavaDialect,
boundParams,
dmo ) );
FactPattern factPattern = new FactPattern( pattern.getObjectType() );
factPattern.setBoundName( pattern.getIdentifier() );
boundParams.put( factPattern.getBoundName(),
factPattern.getFactType() );
parseConstraint( m,
factPattern,
pattern.getConstraint(),
isJavaDialect,
boundParams,
dmo );
fac.setFactPattern( factPattern );
for ( AccumulateDescr.AccumulateFunctionCallDescr func : accumulate.getFunctions() ) {
String funcName = func.getFunction();
boolean first = true;
StringBuilder sb = new StringBuilder();
for ( String param : func.getParams() ) {
if ( first ) {
first = false;
} else {
sb.append( ", " );
}
sb.append( param );
}
fac.setFunction( funcName + "(" + sb + ")" );
break;
}
return fac;
} else if ( patternSource instanceof CollectDescr ) {
CollectDescr collect = (CollectDescr) patternSource;
FromCollectCompositeFactPattern fac = new FromCollectCompositeFactPattern();
fac.setRightPattern( parseBaseDescr( m,
collect.getInputPattern(),
isJavaDialect,
boundParams,
dmo ) );
fac.setFactPattern( getFactPattern( m,
pattern,
isJavaDialect,
boundParams,
dmo ) );
return fac;
} else if ( patternSource instanceof EntryPointDescr ) {
EntryPointDescr entryPoint = (EntryPointDescr) patternSource;
FromEntryPointFactPattern fep = new FromEntryPointFactPattern();
fep.setEntryPointName( entryPoint.getText() );
fep.setFactPattern( getFactPattern( m,
pattern,
isJavaDialect,
boundParams,
dmo ) );
return fep;
} else if ( patternSource instanceof FromDescr ) {
FromDescr from = (FromDescr) patternSource;
FromCompositeFactPattern fcfp = new FromCompositeFactPattern();
FactPattern factPattern = new FactPattern( pattern.getObjectType() );
factPattern.setBoundName( pattern.getIdentifier() );
parseConstraint( m,
factPattern,
pattern.getConstraint(),
isJavaDialect,
boundParams,
dmo );
fcfp.setFactPattern( factPattern );
ExpressionFormLine expression = new ExpressionFormLine();
fcfp.setExpression( expression );
String dataSource = from.getDataSource().toString();
String[] splitSource = dataSource.split( "\\." );
ModelField[] fields = null;
for ( int i = 0; i < splitSource.length; i++ ) {
String sourcePart = splitSource[ i ];
if ( i == 0 ) {
String type = boundParams.get( sourcePart );
expression.appendPart( new ExpressionVariable( sourcePart,
type,
DataType.TYPE_NUMERIC ) );
fields = findFields( m,
dmo,
type );
} else {
ModelField modelField = null;
for ( ModelField field : fields ) {
if ( field.getName().equals( sourcePart ) ) {
modelField = field;
break;
}
}
if ( modelField == null ) {
final String previousClassName = expression.getClassType();
final List<MethodInfo> mis = dmo.getProjectMethodInformation().get( previousClassName );
boolean isMethod = false;
if ( mis != null ) {
for ( MethodInfo mi : mis ) {
if ( mi.getName().equals( sourcePart ) ) {
expression.appendPart( new ExpressionMethod( mi.getName(),
mi.getReturnClassType(),
mi.getGenericType(),
mi.getParametricReturnType() ) );
isMethod = true;
break;
}
}
}
if ( isMethod == false ) {
expression.appendPart( new ExpressionText( sourcePart ) );
}
} else {
expression.appendPart( new ExpressionField( sourcePart,
modelField.getClassName(),
modelField.getType() ) );
fields = findFields( m,
dmo,
modelField.getClassName() );
}
}
}
return fcfp;
}
throw new RuntimeException( "Unknown pattern source " + patternSource );
}
private CompositeFactPattern parseExistentialElementDescr( final RuleModel m,
final ConditionalElementDescr conditionalDescr,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
CompositeFactPattern comp = conditionalDescr instanceof NotDescr ?
new CompositeFactPattern( CompositeFactPattern.COMPOSITE_TYPE_NOT ) :
conditionalDescr instanceof OrDescr ?
new CompositeFactPattern( CompositeFactPattern.COMPOSITE_TYPE_OR ) :
new CompositeFactPattern( CompositeFactPattern.COMPOSITE_TYPE_EXISTS );
addPatternToComposite( m,
conditionalDescr,
comp,
isJavaDialect,
boundParams,
dmo );
IFactPattern[] patterns = comp.getPatterns();
return patterns != null && patterns.length > 0 ? comp : null;
}
private void addPatternToComposite( final RuleModel m,
final ConditionalElementDescr conditionalDescr,
final CompositeFactPattern comp,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
for ( Object descr : conditionalDescr.getDescrs() ) {
if ( descr instanceof PatternDescr ) {
comp.addFactPattern( parsePatternDescr( m,
(PatternDescr) descr,
isJavaDialect,
boundParams,
dmo ) );
} else if ( descr instanceof ConditionalElementDescr ) {
addPatternToComposite( m,
(ConditionalElementDescr) descr,
comp,
isJavaDialect,
boundParams,
dmo );
}
}
}
private void parseConstraint( final RuleModel m,
final FactPattern factPattern,
final ConditionalElementDescr constraint,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
for ( BaseDescr descr : constraint.getDescrs() ) {
if ( descr instanceof ExprConstraintDescr ) {
ExprConstraintDescr exprConstraint = (ExprConstraintDescr) descr;
Expr expr = parseExpr( exprConstraint.getExpression(),
isJavaDialect,
boundParams,
dmo );
factPattern.addConstraint( expr.asFieldConstraint( m,
factPattern ) );
}
}
}
private static String findOperator( final String expr ) {
final Set<String> potentialOperators = new HashSet<String>();
for ( Operator op : Operator.getAllOperators() ) {
String opString = op.getOperatorString();
if ( op.isNegated() ) {
if ( expr.contains( " not " + opString ) ) {
return "not " + opString;
}
}
int opPos = expr.indexOf( opString );
if ( opPos >= 0 && !isInQuote( expr, opPos ) &&
!( Character.isLetter( opString.charAt( 0 ) ) &&
( expr.length() == opPos + opString.length() || Character.isLetter( expr.charAt( opPos + opString.length() ) ) ||
( opPos > 0 && Character.isLetter( expr.charAt( opPos - 1 ) ) ) ) ) ) {
potentialOperators.add( opString );
}
}
String operator = "";
if ( !potentialOperators.isEmpty() ) {
for ( String potentialOperator : potentialOperators ) {
if ( potentialOperator.length() > operator.length() ) {
operator = potentialOperator;
}
}
}
if ( !operator.isEmpty() ) {
return operator;
}
if ( expr.contains( " not in " ) ) {
return "not in";
}
if ( expr.contains( " in " ) ) {
return "in";
}
return null;
}
private static boolean isInQuote( final String expr,
final int pos ) {
boolean isInQuote = false;
for ( int i = pos - 1; i >= 0; i-- ) {
if ( expr.charAt( i ) == '"' ) {
isInQuote = !isInQuote;
}
}
return isInQuote;
}
private static final String[] NULL_OPERATORS = new String[]{ "== null", "!= null" };
private static String findNullOrNotNullOperator( final String expr ) {
for ( String op : NULL_OPERATORS ) {
if ( expr.contains( op ) ) {
return op;
}
}
return null;
}
private void parseRhs( final RuleModel m,
final String rhs,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final ExpandedDRLInfo expandedDRLInfo,
final PackageDataModelOracle dmo ) {
PortableWorkDefinition pwd = null;
Map<String, List<String>> setStatements = new HashMap<String, List<String>>();
Map<String, Integer> setStatementsPosition = new HashMap<String, Integer>();
Map<String, String> factsType = new HashMap<String, String>();
String modifiedVariable = null;
String modifiers = null;
int lineCounter = -1;
String[] lines = rhs.split( "\n" );
for ( String line : lines ) {
lineCounter++;
if ( expandedDRLInfo.hasDsl ) {
String dslLine = expandedDRLInfo.dslStatementsInRhs.get( lineCounter );
while ( dslLine != null ) {
m.addRhsItem( toDSLSentence( expandedDRLInfo.rhsDslPatterns,
dslLine ) );
dslLine = expandedDRLInfo.dslStatementsInRhs.get( ++lineCounter );
}
}
line = line.trim();
if ( modifiedVariable != null ) {
int modifyBlockEnd = line.lastIndexOf( '}' );
if ( modifiers == null ) {
modifiers = modifyBlockEnd > 0 ?
line.substring( line.indexOf( '{' ) + 1,
modifyBlockEnd ).trim() :
line.substring( line.indexOf( '{' ) + 1 ).trim();
} else if ( modifyBlockEnd != 0 ) {
modifiers += modifyBlockEnd > 0 ?
line.substring( 0,
modifyBlockEnd ).trim() :
line;
}
if ( modifyBlockEnd >= 0 ) {
ActionUpdateField action = new ActionUpdateField();
action.setVariable( modifiedVariable );
m.addRhsItem( action );
addModifiersToAction( modifiers,
action,
boundParams,
dmo,
m.getImports(),
isJavaDialect );
modifiedVariable = null;
modifiers = null;
}
} else if ( line.startsWith( "insertLogical" ) ) {
String fact = unwrapParenthesis( line );
String type = getStatementType( fact,
factsType );
if ( type != null ) {
ActionInsertLogicalFact action = new ActionInsertLogicalFact( type );
m.addRhsItem( action );
if ( factsType.containsKey( fact ) ) {
addSettersToAction( setStatements,
fact,
action,
boundParams,
dmo,
m.getImports(),
isJavaDialect );
}
}
} else if ( line.startsWith( "insert" ) ) {
String fact = unwrapParenthesis( line );
String type = getStatementType( fact,
factsType );
if ( type != null ) {
ActionInsertFact action = new ActionInsertFact( type );
m.addRhsItem( action );
if ( factsType.containsKey( fact ) ) {
action.setBoundName( fact );
addSettersToAction( setStatements,
fact,
action,
boundParams,
dmo,
m.getImports(),
isJavaDialect );
}
}
} else if ( line.startsWith( "update" ) ) {
String variable = unwrapParenthesis( line );
ActionUpdateField action = new ActionUpdateField();
action.setVariable( variable );
m.addRhsItem( action );
addSettersToAction( setStatements,
variable,
action,
boundParams,
dmo,
m.getImports(),
isJavaDialect );
} else if ( line.startsWith( "modify" ) ) {
int modifyBlockEnd = line.lastIndexOf( '}' );
if ( modifyBlockEnd > 0 ) {
String variable = line.substring( line.indexOf( '(' ) + 1,
line.indexOf( ')' ) ).trim();
ActionUpdateField action = new ActionUpdateField();
action.setVariable( variable );
m.addRhsItem( action );
addModifiersToAction( line.substring( line.indexOf( '{' ) + 1,
modifyBlockEnd ).trim(),
action,
boundParams,
dmo,
m.getImports(),
isJavaDialect );
} else {
modifiedVariable = line.substring( line.indexOf( '(' ) + 1,
line.indexOf( ')' ) ).trim();
int modifyBlockStart = line.indexOf( '{' );
if ( modifyBlockStart > 0 ) {
modifiers = line.substring( modifyBlockStart + 1 ).trim();
}
}
} else if ( line.startsWith( "retract" ) || line.startsWith( "delete" ) ) {
String variable = unwrapParenthesis( line );
m.addRhsItem( new ActionRetractFact( variable ) );
} else if ( line.startsWith( "org.drools.core.process.instance.impl.WorkItemImpl wiWorkItem" ) ) {
ActionExecuteWorkItem awi = new ActionExecuteWorkItem();
pwd = new PortableWorkDefinition();
pwd.setName( "WorkItem" );
awi.setWorkDefinition( pwd );
m.addRhsItem( awi );
} else if ( line.startsWith( "wiWorkItem.getParameters().put" ) ) {
String statement = line.substring( "wiWorkItem.getParameters().put".length() );
statement = unwrapParenthesis( statement );
int commaPos = statement.indexOf( ',' );
String name = statement.substring( 0,
commaPos ).trim();
String value = statement.substring( commaPos + 1 ).trim();
pwd.addParameter( buildPortableParameterDefinition( name,
value,
boundParams ) );
} else if ( line.startsWith( "wim.internalExecuteWorkItem" ) || line.startsWith( "wiWorkItem.setName" ) ) {
// ignore
} else {
int dotPos = line.indexOf( '.' );
int argStart = line.indexOf( '(' );
if ( dotPos > 0 && argStart > dotPos ) {
String variable = line.substring( 0,
dotPos ).trim();
if ( isJavaIdentifier( variable ) ) {
String methodName = line.substring( dotPos + 1,
argStart ).trim();
if ( isJavaIdentifier( methodName ) ) {
if ( getSettedField( methodName ) != null ) {
List<String> setters = setStatements.get( variable );
if ( setters == null ) {
setters = new ArrayList<String>();
setStatements.put( variable,
setters );
}
setStatementsPosition.put( variable,
lineCounter );
setters.add( line );
} else if ( methodName.equals( "add" ) && expandedDRLInfo.hasGlobal( variable ) ) {
String factName = line.substring( argStart + 1,
line.lastIndexOf( ')' ) ).trim();
ActionGlobalCollectionAdd actionGlobalCollectionAdd = new ActionGlobalCollectionAdd();
actionGlobalCollectionAdd.setGlobalName( variable );
actionGlobalCollectionAdd.setFactName( factName );
m.addRhsItem( actionGlobalCollectionAdd );
} else {
m.addRhsItem( getActionCallMethod( m,
isJavaDialect,
boundParams,
dmo,
line,
variable,
methodName ) );
}
continue;
}
}
}
int eqPos = line.indexOf( '=' );
boolean addFreeFormLine = line.trim().length() > 0;
if ( eqPos > 0 ) {
String field = line.substring( 0,
eqPos ).trim();
if ( "java.text.SimpleDateFormat sdf".equals( field ) || "org.drools.core.process.instance.WorkItemManager wim".equals( field ) ) {
addFreeFormLine = false;
}
String[] split = field.split( " " );
if ( split.length == 2 ) {
factsType.put( split[ 1 ],
split[ 0 ] );
addFreeFormLine &= !isInsertedFact( lines,
lineCounter,
split[ 1 ] );
}
}
if ( addFreeFormLine ) {
FreeFormLine ffl = new FreeFormLine();
ffl.setText( line );
m.addRhsItem( ffl );
}
}
}
for ( Map.Entry<String, List<String>> entry : setStatements.entrySet() ) {
ActionSetField action = new ActionSetField( entry.getKey() );
addSettersToAction( entry.getValue(),
action,
boundParams,
dmo,
m.getImports(),
isJavaDialect );
m.addRhsItem( action,
setStatementsPosition.get( entry.getKey() ) );
}
if ( expandedDRLInfo.hasDsl ) {
String dslLine = expandedDRLInfo.dslStatementsInRhs.get( ++lineCounter );
while ( dslLine != null ) {
m.addRhsItem( toDSLSentence( expandedDRLInfo.rhsDslPatterns,
dslLine ) );
dslLine = expandedDRLInfo.dslStatementsInRhs.get( ++lineCounter );
}
}
}
private ActionCallMethod getActionCallMethod( final RuleModel model,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final String line,
final String variable,
final String methodName ) {
return new ActionCallMethodBuilder( model,
dmo,
isJavaDialect,
boundParams ).get( variable,
methodName,
unwrapParenthesis( line ).split( "," ) );
}
private boolean isInsertedFact( final String[] lines,
final int lineCounter,
final String fact ) {
for ( int i = lineCounter; i < lines.length; i++ ) {
String line = lines[ i ].trim();
if ( line.startsWith( "insert" ) ) {
if ( fact.equals( unwrapParenthesis( line ) ) ) {
return true;
}
}
}
return false;
}
private DSLSentence toDSLSentence( final List<String> dslPatterns,
final String dslLine ) {
DSLSentence dslSentence = new DSLSentence();
for ( String dslPattern : dslPatterns ) {
// Dollar breaks the matcher, need to escape them.
dslPattern = dslPattern.replace( "$",
"\\$" );
//A DSL Pattern can contain Regex itself, for example "When the ages is less than {num:1?[0-9]?[0-9]}"
String regex = dslPattern.replaceAll( "\\{.*?\\}",
"(.*)" );
Matcher matcher = Pattern.compile( regex ).matcher( dslLine );
if ( matcher.matches() ) {
dslPattern = dslPattern.replace( "\\$",
"$" );
dslSentence.setDefinition( dslPattern );
for ( int i = 0; i < matcher.groupCount(); i++ ) {
dslSentence.getValues().get( i ).setValue( matcher.group( i + 1 ) );
}
return dslSentence;
}
}
dslSentence.setDefinition( dslLine );
return dslSentence;
}
private PortableParameterDefinition buildPortableParameterDefinition( final String name,
final String value,
final Map<String, String> boundParams ) {
PortableParameterDefinition paramDef;
String type = boundParams.get( value );
if ( type != null ) {
if ( type.equals( "Boolean" ) ) {
paramDef = new PortableBooleanParameterDefinition();
} else if ( type.equals( "String" ) ) {
paramDef = new PortableStringParameterDefinition();
} else if ( type.equals( "Float" ) ) {
paramDef = new PortableBooleanParameterDefinition();
} else if ( type.equals( "Integer" ) ) {
paramDef = new PortableIntegerParameterDefinition();
} else {
paramDef = new PortableObjectParameterDefinition();
}
( (HasBinding) paramDef ).setBinding( value );
} else if ( value.equals( "true" ) || value.equals( "false" ) || value.equals( "Boolean.TRUE" ) || value.equals( "Boolean.FALSE" ) ) {
paramDef = new PortableBooleanParameterDefinition();
boolean b = value.equals( "true" ) || value.equals( "Boolean.TRUE" );
( (PortableBooleanParameterDefinition) paramDef ).setValue( b );
} else if ( value.startsWith( "\"" ) ) {
paramDef = new PortableStringParameterDefinition();
( (PortableStringParameterDefinition) paramDef ).setValue( value.substring( 1,
value.length() - 1 ) );
} else if ( Character.isDigit( value.charAt( 0 ) ) ) {
if ( value.endsWith( "f" ) ) {
paramDef = new PortableFloatParameterDefinition();
( (PortableFloatParameterDefinition) paramDef ).setValue( Float.parseFloat( value ) );
} else {
paramDef = new PortableIntegerParameterDefinition();
( (PortableIntegerParameterDefinition) paramDef ).setValue( Integer.parseInt( value ) );
}
} else {
throw new RuntimeException( "Unknown parameter " + value );
}
paramDef.setName( name.substring( 1,
name.length() - 1 ) );
return paramDef;
}
private void addSettersToAction( final Map<String, List<String>> setStatements,
final String variable,
final ActionFieldList action,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final Imports imports,
final boolean isJavaDialect ) {
addSettersToAction( setStatements.remove( variable ),
action,
boundParams,
dmo,
imports,
isJavaDialect );
}
private void addSettersToAction( final List<String> setters,
final ActionFieldList action,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final Imports imports,
final boolean isJavaDialect ) {
if ( setters != null ) {
for ( String statement : setters ) {
int dotPos = statement.indexOf( '.' );
int argStart = statement.indexOf( '(' );
String methodName = statement.substring( dotPos + 1,
argStart ).trim();
addSetterToAction( action,
boundParams,
dmo,
imports,
isJavaDialect,
statement,
methodName );
}
}
}
private void addModifiersToAction( final String modifiers,
final ActionFieldList action,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final Imports imports,
final boolean isJavaDialect ) {
for ( String statement : splitArgumentsList( modifiers ) ) {
int argStart = statement.indexOf( '(' );
String methodName = statement.substring( 0,
argStart ).trim();
addSetterToAction( action,
boundParams,
dmo,
imports,
isJavaDialect,
statement,
methodName );
}
}
private void addSetterToAction( final ActionFieldList action,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final Imports imports,
final boolean isJavaDialect,
final String statement,
final String methodName ) {
String field = getSettedField( methodName );
String value = unwrapParenthesis( statement );
String dataType = inferDataType( action,
field,
boundParams,
dmo,
imports );
if ( dataType == null ) {
dataType = inferDataType( value,
boundParams,
isJavaDialect );
}
action.addFieldValue( buildFieldValue( isJavaDialect,
field,
value,
dataType,
boundParams ) );
}
private ActionFieldValue buildFieldValue( final boolean isJavaDialect,
String field,
final String value,
final String dataType,
final Map<String, String> boundParams ) {
if ( value.contains( "wiWorkItem.getResult" ) ) {
field = field.substring( 0, 1 ).toUpperCase() + field.substring( 1 );
String wiParam = field.substring( "Results".length() );
if ( wiParam.equals( "BooleanResult" ) ) {
return new ActionWorkItemFieldValue( field,
DataType.TYPE_BOOLEAN,
"WorkItem",
wiParam,
Boolean.class.getName() );
} else if ( wiParam.equals( "StringResult" ) ) {
return new ActionWorkItemFieldValue( field,
DataType.TYPE_STRING,
"WorkItem",
wiParam,
String.class.getName() );
} else if ( wiParam.equals( "IntegerResult" ) ) {
return new ActionWorkItemFieldValue( field,
DataType.TYPE_NUMERIC_INTEGER,
"WorkItem",
wiParam,
Integer.class.getName() );
} else if ( wiParam.equals( "FloatResult" ) ) {
return new ActionWorkItemFieldValue( field,
DataType.TYPE_NUMERIC_FLOAT,
"WorkItem",
wiParam,
Float.class.getName() );
}
}
ActionFieldValue fieldValue = new ActionFieldValue( field,
adjustParam( dataType,
value,
boundParams,
isJavaDialect ),
dataType );
fieldValue.setNature( inferFieldNature( dataType,
value,
boundParams,
isJavaDialect ) );
return fieldValue;
}
private boolean isJavaIdentifier( final String name ) {
if ( name == null || name.length() == 0 || !Character.isJavaIdentifierStart( name.charAt( 0 ) ) ) {
return false;
}
for ( int i = 1; i < name.length(); i++ ) {
if ( !Character.isJavaIdentifierPart( name.charAt( i ) ) ) {
return false;
}
}
return true;
}
private String getSettedField( final String methodName ) {
if ( methodName.length() > 3 && methodName.startsWith( "set" ) ) {
String field = methodName.substring( 3 );
if ( Character.isUpperCase( field.charAt( 0 ) ) ) {
return field.substring( 0,
1 ).toLowerCase() + field.substring( 1 );
} else {
return field;
}
}
return null;
}
private String getStatementType( final String fact,
final Map<String, String> factsType ) {
String type = null;
if ( fact.startsWith( "new " ) ) {
String inserted = fact.substring( 4 ).trim();
if ( inserted.endsWith( "()" ) ) {
type = inserted.substring( 0,
inserted.length() - 2 ).trim();
}
} else {
type = factsType.get( fact );
}
return type;
}
private Expr parseExpr( final String expr,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
List<String> splittedExpr = splitExpression( expr );
if ( splittedExpr.size() == 1 ) {
String singleExpr = splittedExpr.get( 0 );
if ( singleExpr.startsWith( "(" ) ) {
return parseExpr( singleExpr.substring( 1 ),
isJavaDialect,
boundParams,
dmo );
} else if ( singleExpr.startsWith( "eval" ) ) {
return new EvalExpr( unwrapParenthesis( singleExpr ) );
} else {
return new SimpleExpr( singleExpr,
isJavaDialect,
boundParams,
dmo );
}
}
ComplexExpr complexExpr = new ComplexExpr( splittedExpr.get( 1 ) );
for ( int i = 0; i < splittedExpr.size(); i += 2 ) {
complexExpr.subExprs.add( parseExpr( splittedExpr.get( i ),
isJavaDialect,
boundParams,
dmo ) );
}
return complexExpr;
}
private enum SplitterState {
START, EXPR, PIPE, OR, AMPERSAND, AND, NESTED
}
private List<String> splitExpression( final String expr ) {
List<String> splittedExpr = new ArrayList<String>();
int nestingLevel = 0;
SplitterState status = SplitterState.START;
StringBuilder sb = new StringBuilder();
for ( char ch : expr.toCharArray() ) {
switch ( status ) {
case START:
if ( ch == '(' ) {
status = SplitterState.NESTED;
nestingLevel++;
} else {
status = SplitterState.EXPR;
sb.append( ch );
}
break;
case EXPR:
if ( ch == '|' ) {
status = SplitterState.PIPE;
} else if ( ch == '&' ) {
status = SplitterState.AMPERSAND;
} else {
sb.append( ch );
}
break;
case PIPE:
if ( ch == '|' ) {
status = SplitterState.OR;
} else {
status = SplitterState.EXPR;
sb.append( '|' ).append( ch );
}
break;
case AMPERSAND:
if ( ch == '&' ) {
status = SplitterState.AND;
} else {
status = SplitterState.EXPR;
sb.append( '&' ).append( ch );
}
break;
case OR:
case AND:
if ( ch == '=' || ch == '!' || ch == '<' || ch == '>' ) {
status = SplitterState.EXPR;
sb.append( status == SplitterState.AND ? "&& " : "|| " );
sb.append( ch );
} else if ( Character.isJavaIdentifierStart( ch ) ) {
String currentExpr = sb.toString().trim();
if ( currentExpr.length() > 0 ) {
splittedExpr.add( currentExpr );
}
splittedExpr.add( status == SplitterState.AND ? "&&" : "||" );
status = SplitterState.EXPR;
sb = new StringBuilder();
sb.append( ch );
} else if ( ch == '(' ) {
String currentExpr = sb.toString().trim();
if ( currentExpr.length() > 0 ) {
splittedExpr.add( currentExpr );
}
splittedExpr.add( status == SplitterState.AND ? "&&" : "||" );
status = SplitterState.NESTED;
nestingLevel++;
sb = new StringBuilder();
sb.append( ch );
}
break;
case NESTED:
if ( ch == '(' ) {
nestingLevel++;
sb.append( ch );
} else if ( ch == ')' ) {
nestingLevel--;
if ( nestingLevel == 0 ) {
String currentExpr = sb.toString().trim();
if ( currentExpr.length() > 0 ) {
splittedExpr.add( "(" + currentExpr );
}
status = SplitterState.EXPR;
sb = new StringBuilder();
} else {
sb.append( ch );
}
} else {
sb.append( ch );
}
break;
}
}
String currentExpr = sb.toString().trim();
if ( currentExpr.length() > 0 ) {
splittedExpr.add( currentExpr );
}
return splittedExpr;
}
private interface Expr {
FieldConstraint asFieldConstraint( final RuleModel m,
final FactPattern factPattern );
}
private static class SimpleExpr implements Expr {
private final String expr;
private final boolean isJavaDialect;
private final Map<String, String> boundParams;
private final PackageDataModelOracle dmo;
private SimpleExpr( final String expr,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo ) {
this.expr = expr;
this.isJavaDialect = isJavaDialect;
this.boundParams = boundParams;
this.dmo = dmo;
}
public FieldConstraint asFieldConstraint( final RuleModel m,
final FactPattern factPattern ) {
String fieldName = expr;
String value = null;
String operator = findNullOrNotNullOperator( expr );
if ( operator != null ) {
int opPos = expr.indexOf( operator );
fieldName = expr.substring( 0,
opPos ).trim();
} else {
operator = findOperator( expr );
if ( operator != null ) {
int opPos = expr.lastIndexOf( operator );
fieldName = expr.substring( 0,
opPos ).trim();
value = expr.substring( opPos + operator.length(),
expr.length() ).trim();
}
}
boolean isExpression = fieldName.contains( "." ) || fieldName.endsWith( "()" );
return createFieldConstraint( m,
factPattern,
fieldName,
value,
operator,
isExpression );
}
private SingleFieldConstraint createNullCheckFieldConstraint( final RuleModel m,
final FactPattern factPattern,
final String fieldName ) {
return createFieldConstraint( m,
factPattern,
fieldName,
null,
null,
true );
}
private SingleFieldConstraint createFieldConstraint( final RuleModel m,
final FactPattern factPattern,
final String fieldName,
String value,
final String operator,
final boolean isExpression ) {
String operatorParams = null;
if ( value != null && value.startsWith( "[" ) ) {
int endSquare = value.indexOf( ']' );
operatorParams = value.substring( 1,
endSquare ).trim();
value = value.substring( endSquare + 1 ).trim();
}
SingleFieldConstraint fieldConstraint = isExpression ?
createExpressionBuilderConstraint( m,
factPattern,
fieldName,
operator,
value ) :
createSingleFieldConstraint( m,
factPattern,
fieldName,
operator,
value );
if ( operatorParams != null ) {
int i = 0;
for ( String param : operatorParams.split( "," ) ) {
fieldConstraint.setParameter( "" + i++,
param.trim() );
}
fieldConstraint.setParameter( "org.drools.workbench.models.commons.backend.rule.visibleParameterSet",
"" + i );
fieldConstraint.setParameter( "org.drools.workbench.models.commons.backend.rule.operatorParameterGenerator",
"org.drools.workbench.models.commons.backend.rule.CEPOperatorParameterDRLBuilder" );
}
if ( fieldName.equals( "this" ) && ( operator == null || operator.equals( "!= null" ) ) ) {
fieldConstraint.setFieldType( DataType.TYPE_THIS );
}
fieldConstraint.setFactType( factPattern.getFactType() );
ModelField field = findField( findFields( m,
dmo,
factPattern.getFactType() ),
fieldConstraint.getFieldName() );
if ( field != null && ( fieldConstraint.getFieldType() == null || fieldConstraint.getFieldType().trim().length() == 0 ) ) {
fieldConstraint.setFieldType( field.getType() );
}
return fieldConstraint;
}
private SingleFieldConstraint createExpressionBuilderConstraint( final RuleModel m,
final FactPattern factPattern,
final String fieldName,
final String operator,
final String value ) {
// TODO: we should find a way to know when the expression uses a getter and in this case create a plain SingleFieldConstraint
//int dotPos = fieldName.lastIndexOf('.');
//SingleFieldConstraint con = createSingleFieldConstraint(dotPos > 0 ? fieldName.substring(dotPos+1) : fieldName, operator, value);
SingleFieldConstraint con = createSingleFieldConstraintEBLeftSide( m,
factPattern,
fieldName,
operator,
value );
return con;
}
private SingleFieldConstraint createSingleFieldConstraint( final RuleModel m,
final FactPattern factPattern,
String fieldName,
final String operator,
final String value ) {
SingleFieldConstraint con = new SingleFieldConstraint();
fieldName = setFieldBindingOnContraint( factPattern.getFactType(),
fieldName,
m,
con,
boundParams );
con.setFieldName( fieldName );
setOperatorAndValueOnConstraint( m,
operator,
value,
factPattern,
con );
//Setup parent relationships for SingleFieldConstraints
for ( FieldConstraint fieldConstraint : factPattern.getFieldConstraints() ) {
if ( fieldConstraint instanceof SingleFieldConstraint ) {
SingleFieldConstraint sfc = (SingleFieldConstraint) fieldConstraint;
if ( sfc.getOperator() != null && sfc.getOperator().equals( "!= null" ) ) {
int parentPos = fieldName.indexOf( sfc.getFieldName() + "." );
if ( parentPos >= 0 && !fieldName.substring( parentPos + sfc.getFieldName().length() + 1 ).contains( "." ) ) {
con.setParent( sfc );
break;
}
}
}
}
if ( con.getParent() == null ) {
con.setParent( createParentFor( m, factPattern, fieldName ) );
}
return con;
}
private SingleFieldConstraintEBLeftSide createSingleFieldConstraintEBLeftSide( final RuleModel m,
final FactPattern factPattern,
String fieldName,
final String operator,
final String value ) {
SingleFieldConstraintEBLeftSide con = new SingleFieldConstraintEBLeftSide();
fieldName = setFieldBindingOnContraint( factPattern.getFactType(),
fieldName,
m,
con,
boundParams );
String classType = getFQFactType( m,
factPattern.getFactType() );
con.getExpressionLeftSide().appendPart( new ExpressionUnboundFact( factPattern ) );
parseExpression( m,
classType,
fieldName,
con.getExpressionLeftSide() );
setOperatorAndValueOnConstraint( m,
operator,
value,
factPattern,
con );
return con;
}
private ExpressionFormLine parseExpression( final RuleModel m,
String factType,
final String fieldName,
final ExpressionFormLine expression ) {
String[] splits = fieldName.split( "\\." );
boolean isBoundParam = false;
if ( factType == null ) {
factType = getFQFactType( m,
boundParams.get( splits[ 0 ].trim() ) );
isBoundParam = true;
}
//An ExpressionPart can be a Field or a Method
ModelField[] typeFields = findFields( m,
dmo,
factType );
List<MethodInfo> methodInfos = getMethodInfosForType( m,
dmo,
factType );
//Handle all but last expression part
for ( int i = 0; i < splits.length - 1; i++ ) {
String expressionPart = splits[ i ];
//The first part of the expression may be a bound variable
if ( boundParams.containsKey( expressionPart ) ) {
factType = getFQFactType( m,
boundParams.get( expressionPart ) );
isBoundParam = true;
typeFields = findFields( m,
dmo,
factType );
methodInfos = getMethodInfosForType( m,
dmo,
factType );
}
if ( "this".equals( expressionPart ) ) {
expression.appendPart( new ExpressionField( expressionPart,
factType,
DataType.TYPE_THIS ) );
} else if ( isBoundParam ) {
ModelField currentFact = findFact( dmo.getProjectModelFields(),
factType );
expression.appendPart( new ExpressionVariable( expressionPart,
currentFact.getClassName(),
currentFact.getType() ) );
isBoundParam = false;
} else {
//An ExpressionPart can be a Field or a Method
String currentClassName = null;
ModelField currentField = findField( typeFields,
expressionPart );
if ( currentField != null ) {
currentClassName = currentField.getClassName();
}
MethodInfo currentMethodInfo = findMethodInfo( methodInfos,
expressionPart );
if ( currentMethodInfo != null ) {
currentClassName = currentMethodInfo.getReturnClassType();
}
processExpressionPart( m,
factType,
currentField,
currentMethodInfo,
expression,
expressionPart );
//Refresh field and method information based on current expression part
typeFields = findFields( m,
dmo,
currentClassName );
methodInfos = getMethodInfosForType( m,
dmo,
currentClassName );
}
}
//Handle last expression part
String expressionPart = splits[ splits.length - 1 ];
ModelField currentField = findField( typeFields,
expressionPart );
MethodInfo currentMethodInfo = findMethodInfo( methodInfos,
expressionPart );
processExpressionPart( m,
factType,
currentField,
currentMethodInfo,
expression,
expressionPart );
return expression;
}
private void processExpressionPart( final RuleModel m,
final String factType,
final ModelField currentField,
final MethodInfo currentMethodInfo,
final ExpressionFormLine expression,
final String expressionPart ) {
if ( currentField == null ) {
boolean isMethod = currentMethodInfo != null;
if ( isMethod ) {
final ExpressionMethod em = new ExpressionMethod( currentMethodInfo.getName(),
currentMethodInfo.getReturnClassType(),
currentMethodInfo.getGenericType(),
currentMethodInfo.getParametricReturnType() );
//Add applicable parameter values
final List<String> parameters = parseExpressionParameters( expressionPart );
for ( int index = 0; index < currentMethodInfo.getParams().size(); index++ ) {
final String paramDataType = currentMethodInfo.getParams().get( index );
final String paramValue = getParameterValue( paramDataType,
parameters,
index );
if ( paramValue != null ) {
final ExpressionFormLine param = new ExpressionFormLine( index );
param.appendPart( new ExpressionMethodParameter( paramValue,
paramDataType,
paramDataType ) );
em.putParam( paramDataType,
param );
}
}
expression.appendPart( em );
} else {
expression.appendPart( new ExpressionText( expressionPart ) );
}
} else if ( "Collection".equals( currentField.getType() ) ) {
expression.appendPart( new ExpressionCollection( expressionPart,
currentField.getClassName(),
currentField.getType(),
dmo.getProjectFieldParametersType().get( factType + "#" + expressionPart ) )
);
} else {
expression.appendPart( new ExpressionField( expressionPart,
currentField.getClassName(),
currentField.getType() ) );
}
}
private String getParameterValue( final String paramDataType,
final List<String> parameters,
final int index ) {
if ( parameters == null || parameters.isEmpty() ) {
return null;
}
if ( index < 0 || index > parameters.size() - 1 ) {
return null;
}
return RuleModelPersistenceHelper.adjustParam( paramDataType,
parameters.get( index ).trim(),
boundParams,
isJavaDialect );
}
private String getFQFactType( final RuleModel ruleModel,
final String factType ) {
Set<String> factTypes = dmo.getProjectModelFields().keySet();
if ( factTypes.contains( ruleModel.getPackageName() + "." + factType ) ) {
return ruleModel.getPackageName() + "." + factType;
}
for ( String item : ruleModel.getImports().getImportStrings() ) {
if ( item.endsWith( "." + factType ) ) {
return item;
}
}
for ( String type : factTypes ) {
if ( type.endsWith( "." + factType ) ) {
return type;
}
}
return factType;
}
private ModelField findFact( final Map<String, ModelField[]> modelFields,
final String factType ) {
final ModelField[] typeFields = modelFields.get( factType );
if ( typeFields == null ) {
return null;
}
for ( ModelField typeField : typeFields ) {
if ( typeField.getType().equals( DataType.TYPE_THIS ) ) {
return typeField;
}
}
return null;
}
private SingleFieldConstraint createParentFor( final RuleModel m,
final FactPattern factPattern,
final String fieldName ) {
int dotPos = fieldName.lastIndexOf( '.' );
if ( dotPos > 0 ) {
SingleFieldConstraint constraint = createNullCheckFieldConstraint( m,
factPattern,
fieldName.substring( 0,
dotPos ) );
factPattern.addConstraint( constraint );
return constraint;
}
return null;
}
private String setFieldBindingOnContraint( final String factType,
String fieldName,
final RuleModel model,
final SingleFieldConstraint con,
final Map<String, String> boundParams ) {
int colonPos = fieldName.indexOf( ':' );
if ( colonPos > 0 ) {
String fieldBinding = fieldName.substring( 0,
colonPos ).trim();
con.setFieldBinding( fieldBinding );
fieldName = fieldName.substring( colonPos + 1 ).trim();
ModelField[] fields = findFields( model,
dmo,
factType );
if ( fields != null ) {
for ( ModelField field : fields ) {
if ( field.getName().equals( fieldName ) ) {
boundParams.put( fieldBinding,
field.getType() );
}
}
}
}
return fieldName;
}
private String setOperatorAndValueOnConstraint( final RuleModel m,
final String operator,
final String value,
final FactPattern factPattern,
final SingleFieldConstraint con ) {
con.setOperator( operator );
String type = null;
boolean isAnd = false;
String[] splittedValue = new String[ 0 ];
if ( value != null ) {
isAnd = value.contains( "&&" );
splittedValue = isAnd ? value.split( "\\&\\&" ) : value.split( "\\|\\|" );
type = setValueOnConstraint( m,
operator,
factPattern,
con,
splittedValue[ 0 ].trim() );
}
if ( splittedValue.length > 1 ) {
ConnectiveConstraint[] connectiveConstraints = new ConnectiveConstraint[ splittedValue.length - 1 ];
for ( int i = 0; i < connectiveConstraints.length; i++ ) {
String constraint = splittedValue[ i + 1 ].trim();
String connectiveOperator = findOperator( constraint );
String connectiveValue = constraint.substring( connectiveOperator.length() ).trim();
connectiveConstraints[ i ] = new ConnectiveConstraint();
connectiveConstraints[ i ].setOperator( ( isAnd ? "&& " : "|| " ) + connectiveOperator );
setValueOnConstraint( m,
operator,
factPattern,
connectiveConstraints[ i ],
connectiveValue );
}
con.setConnectives( connectiveConstraints );
}
return type;
}
private String setValueOnConstraint( final RuleModel m,
final String operator,
final FactPattern factPattern,
final BaseSingleFieldConstraint con,
String value ) {
String type = null;
if ( value.startsWith( "\"" ) ) {
type = DataType.TYPE_STRING;
con.setConstraintValueType( SingleFieldConstraint.TYPE_LITERAL );
con.setValue( value.substring( 1,
value.length() - 1 ) );
} else if ( value.startsWith( "(" ) ) {
if ( operator != null && operator.contains( "in" ) ) {
value = unwrapParenthesis( value );
type = value.startsWith( "\"" ) ? DataType.TYPE_STRING : DataType.TYPE_NUMERIC_INTEGER;
con.setConstraintValueType( SingleFieldConstraint.TYPE_LITERAL );
con.setValue( value );
} else {
con.setConstraintValueType( SingleFieldConstraint.TYPE_RET_VALUE );
con.setValue( unwrapParenthesis( value ) );
}
} else {
if ( !Character.isDigit( value.charAt( 0 ) ) ) {
if ( value.equals( "true" ) || value.equals( "false" ) ) {
type = DataType.TYPE_BOOLEAN;
con.setConstraintValueType( BaseSingleFieldConstraint.TYPE_ENUM );
} else if ( isEnumerationValue( m,
factPattern,
con ) ) {
type = DataType.TYPE_COMPARABLE;
con.setConstraintValueType( SingleFieldConstraint.TYPE_ENUM );
} else if ( value.indexOf( '.' ) > 0 && boundParams.containsKey( value.substring( 0,
value.indexOf( '.' ) ).trim() ) ) {
con.setExpressionValue( parseExpression( m,
null,
value,
new ExpressionFormLine() ) );
con.setConstraintValueType( BaseSingleFieldConstraint.TYPE_EXPR_BUILDER_VALUE );
value = "";
} else {
con.setConstraintValueType( SingleFieldConstraint.TYPE_VARIABLE );
}
} else {
if ( value.endsWith( "I" ) ) {
type = DataType.TYPE_NUMERIC_BIGINTEGER;
value = value.substring( 0,
value.length() - 1 );
} else if ( value.endsWith( "B" ) ) {
type = DataType.TYPE_NUMERIC_BIGDECIMAL;
value = value.substring( 0,
value.length() - 1 );
} else if ( value.endsWith( "f" ) ) {
type = DataType.TYPE_NUMERIC_FLOAT;
} else if ( value.endsWith( "d" ) ) {
type = DataType.TYPE_NUMERIC_DOUBLE;
} else {
type = DataType.TYPE_NUMERIC_INTEGER;
}
con.setConstraintValueType( SingleFieldConstraint.TYPE_LITERAL );
}
con.setValue( value );
}
if ( con instanceof SingleFieldConstraint ) {
( (SingleFieldConstraint) con ).setFieldType( type );
} else if ( con instanceof ConnectiveConstraint ) {
( (ConnectiveConstraint) con ).setFieldType( type );
}
return type;
}
private boolean isEnumerationValue( final RuleModel ruleModel,
final FactPattern factPattern,
final BaseSingleFieldConstraint con ) {
String factType = null;
String fieldName = null;
if ( con instanceof SingleFieldConstraintEBLeftSide ) {
SingleFieldConstraintEBLeftSide sfcex = (SingleFieldConstraintEBLeftSide) con;
List<ExpressionPart> sfcexParts = sfcex.getExpressionLeftSide().getParts();
factType = sfcexParts.get( sfcexParts.size() - 1 ).getPrevious().getClassType();
fieldName = sfcex.getFieldName();
} else if ( con instanceof SingleFieldConstraint ) {
factType = factPattern.getFactType();
fieldName = ( (SingleFieldConstraint) con ).getFieldName();
} else if ( con instanceof ConnectiveConstraint ) {
factType = factPattern.getFactType();
fieldName = ( (ConnectiveConstraint) con ).getFieldName();
}
if ( factType == null || fieldName == null ) {
return false;
}
final String fullyQualifiedFactType = getFQFactType( ruleModel,
factType );
final String key = fullyQualifiedFactType + "#" + fieldName;
final Map<String, String[]> projectJavaEnumDefinitions = dmo.getProjectJavaEnumDefinitions();
return projectJavaEnumDefinitions.containsKey( key );
}
}
private static class ComplexExpr implements Expr {
private final List<Expr> subExprs = new ArrayList<Expr>();
private final String connector;
private ComplexExpr( final String connector ) {
this.connector = connector;
}
public FieldConstraint asFieldConstraint( final RuleModel m,
final FactPattern factPattern ) {
CompositeFieldConstraint comp = new CompositeFieldConstraint();
comp.setCompositeJunctionType( connector.equals( "&&" ) ? CompositeFieldConstraint.COMPOSITE_TYPE_AND : CompositeFieldConstraint.COMPOSITE_TYPE_OR );
for ( Expr expr : subExprs ) {
comp.addConstraint( expr.asFieldConstraint( m,
factPattern ) );
}
return comp;
}
}
private static class EvalExpr implements Expr {
private final String expr;
private EvalExpr( final String expr ) {
this.expr = expr;
}
public FieldConstraint asFieldConstraint( final RuleModel m,
final FactPattern factPattern ) {
SingleFieldConstraint con = new SingleFieldConstraint();
con.setConstraintValueType( SingleFieldConstraint.TYPE_PREDICATE );
con.setValue( expr );
return con;
}
}
}