/*
* Copyright 2012 Red Hat, Inc. and/or its affiliates.
*
* 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.Collection;
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 java.util.stream.Collectors;
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.ExistsDescr;
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.commons.backend.rule.exception.RuleModelDRLPersistenceException;
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.util.PortablePreconditions;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.drools.core.util.StringUtils.splitArgumentsList;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.adjustParam;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.findField;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.findFields;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.findMethodInfo;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.getMethodInfosForType;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.getSimpleFactType;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.inferDataType;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.inferFieldNature;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.parseExpressionParameters;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.removeNumericSuffix;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.unwrapParenthesis;
import static org.drools.workbench.models.commons.backend.rule.RuleModelPersistenceHelper.unwrapTemplateKey;
/**
* 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();
private static final Logger log = LoggerFactory.getLogger(RuleModelDRLPersistenceImpl.class);
//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)
*/
@Override
public String marshal(final RuleModel model) {
return marshalRule(model,
Collections.emptyList());
}
@Override
public String marshal(final RuleModel model,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
PortablePreconditions.checkNotNull("extensions",
extensions);
return marshalRule(model,
extensions);
}
protected String marshalRule(final RuleModel model,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
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(),
extensions);
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,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
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);
if (isDSLEnhanced) {
buf.append(">");
}
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,
extensions);
//Reconcile ActionSetField and ActionUpdateField calls
final List<IAction> actions = new ArrayList<IAction>();
for (IAction action : model.rhs) {
if (action instanceof ActionCallMethod) {
actions.add(action);
} else 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;
}
}
for (IAction action : model.rhs) {
List<RuleModelIActionPersistenceExtension> matchingExtensions = extensions.stream().filter(e -> e.accept(action)).collect(Collectors.toList());
if (matchingExtensions.isEmpty()) {
actionVisitor.visit(action);
} else if (matchingExtensions.size() > 1) {
throw new RuleModelDRLPersistenceException("Ambiguous RuleModelIActionPersistenceExtension implementations (" + matchingExtensions + ") found for action " + action);
} else {
buf.append(indentation)
.append(matchingExtensions.get(0).marshal(action))
.append("\n");
}
}
}
}
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,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
return new RHSActionVisitor(isDSLEnhanced,
bindingsPatterns,
bindingsFields,
constraintValueBuilder,
generatorContextFactory,
extensions,
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());
// DROOLS-1308 - wraps from pattern in parenthesis
if (!isSubPattern) {
buf.append("(");
}
generateFactPattern(pattern.getFactPattern(),
gctx);
buf.append(" from ");
renderExpression(pattern.getExpression());
if (!isSubPattern) {
buf.append(")");
}
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(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(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(operator,
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(" ");
}
protected 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(constraintValueBuilder);
buf.append(expression.getText(visitor));
buf.append(" ");
}
}
protected void buildTemplateFieldValue(final String operator,
final int type,
final String fieldType,
final String value,
final StringBuilder buf) {
if (OperatorsOracle.operatorRequiresList(operator)) {
buf.append(" ");
constraintValueBuilder.buildLHSFieldValue(buf,
type,
DataType.TYPE_COLLECTION,
"@{makeValueList(" + value + ")}");
buf.append(" ");
} else {
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 Collection<RuleModelIActionPersistenceExtension> extensions;
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 Collection<RuleModelIActionPersistenceExtension> extensions,
final StringBuilder b,
final String indentation) {
this.isDSLEnhanced = isDSLEnhanced;
this.bindingsPatterns = bindingsPatterns;
this.bindingsFields = bindingsFields;
this.constraintValueBuilder = constraintValueBuilder;
this.generatorContextFactory = generatorContextFactory;
this.extensions = extensions;
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)
*/
@Override
public RuleModel unmarshal(final String str,
final List<String> globals,
final PackageDataModelOracle dmo) {
return unmarshal(str,
globals,
dmo,
Collections.emptyList());
}
@Override
public RuleModel unmarshal(final String str,
final List<String> globals,
final PackageDataModelOracle dmo,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
PortablePreconditions.checkNotNull("extensions",
extensions);
if (str == null || str.isEmpty()) {
return new RuleModel();
}
try {
return getRuleModel(preprocessDRL(str,
false).registerGlobals(dmo,
globals),
dmo,
extensions);
} catch (RuleModelDRLPersistenceException e) {
throw e;
} catch (Exception e) {
log.info("Unable to parse source Drl. Falling back to simple parser. \n" + str);
return getSimpleRuleModel(str);
}
}
public RuleModel unmarshalUsingDSL(final String str,
final List<String> globals,
final PackageDataModelOracle dmo,
final String... dsls) {
return unmarshalUsingDSL(str,
globals,
dmo,
Collections.emptyList(),
dsls);
}
@Override
public RuleModel unmarshalUsingDSL(String str,
List<String> globals,
PackageDataModelOracle dmo,
Collection<RuleModelIActionPersistenceExtension> extensions,
String... dsls) {
if (str == null || str.isEmpty()) {
return new RuleModel();
}
try {
return getRuleModel(parseDSLs(preprocessDRL(str,
true),
dsls).registerGlobals(dmo,
globals),
dmo,
extensions);
} catch (RuleModelDRLPersistenceException e) {
throw e;
} catch (Exception e) {
log.info("Unable to parse source Drl. Falling back to simple parser. \n" + str);
return getSimpleRuleModel(str);
}
}
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]")) {
final String dslDefinition = dslPattern.substring("[when]".length());
expandedDRLInfo.lhsDslPatterns.add(new SimpleDSLSentence(extractDslPattern(dslDefinition),
extractDslDrl(dslDefinition)));
} else if (dslPattern.startsWith("[then]")) {
final String dslDefinition = dslPattern.substring("[then]".length());
expandedDRLInfo.rhsDslPatterns.add(new SimpleDSLSentence(extractDslPattern(dslDefinition),
extractDslDrl(dslDefinition)));
} else if (dslPattern.startsWith("[")) {
final String dslDefinition = removeDslTopics(dslPattern);
expandedDRLInfo.lhsDslPatterns.add(new SimpleDSLSentence(extractDslPattern(dslDefinition),
extractDslDrl(dslDefinition)));
expandedDRLInfo.rhsDslPatterns.add(new SimpleDSLSentence(extractDslPattern(dslDefinition),
extractDslDrl(dslDefinition)));
}
}
}
}
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 String extractDslDrl(final String line) {
return line.substring(line.indexOf('=') + 1).trim();
}
private RuleModel getRuleModel(final ExpandedDRLInfo expandedDRLInfo,
final PackageDataModelOracle dmo,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
//De-serialize model
RuleDescr ruleDescr = parseDrl(expandedDRLInfo);
RuleModel model = new RuleModel();
model.name = ruleDescr.getName();
model.parentName = ruleDescr.getParentName();
for (AnnotationDescr annotation : ruleDescr.getAnnotations()) {
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,
extensions);
return model;
}
private ExpandedDRLInfo preprocessDRL(final String str,
final boolean hasDsl) {
StringBuilder drl = new StringBuilder();
String thenLine = "";
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 (line.trim().length() == 0) {
continue;
}
if (ruleSection == RuleSection.HEADER) {
drl.append(line).append("\n");
if (isLHSStartMarker(line)) {
ruleSection = RuleSection.LHS;
}
continue;
}
if (ruleSection == RuleSection.LHS && isRHSStartMarker(line)) {
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 boolean isLHSStartMarker(final String line) {
final String lhsMarker = line.trim() + " ";
return lhsMarker.startsWith("when ");
}
private boolean isRHSStartMarker(final String line) {
final String rhsMarker = line.trim() + " ";
return rhsMarker.startsWith("then ");
}
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);
}
} else {
drl.append("end");
}
}
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 = "";
for (String statement : rhsStatements) {
String trimmed = statement.trim();
if (trimmed.endsWith("end")) {
trimmed = trimmed.substring(0,
trimmed.length() - 3).trim();
}
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) && !containsComment(lhs);
}
private boolean containsComment(final String lhs) {
//Check if there are any comments not inside Strings. Comments are ignored by the DrlParser
//and hence lines containing comments will be "lost" unless we handle such lines as
//FreeFormLines when unmarshalling DRL.
String _lhs = lhs;
Pattern pattern = Pattern.compile("\"([^\"]*)\"");
Matcher matcher = pattern.matcher(lhs);
while (matcher.find()) {
final String text = matcher.group(1);
final int index = _lhs.indexOf(text);
_lhs = _lhs.substring(0,
index) + _lhs.substring(index + text.length());
}
return _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<SimpleDSLSentence> lhsDslPatterns;
private List<SimpleDSLSentence> 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<SimpleDSLSentence>();
rhsDslPatterns = new ArrayList<SimpleDSLSentence>();
}
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);
if (drlParser.hasErrors()) {
throw new RuleModelUnmarshallingException();
}
} 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 (pattern.getIdentifier() != null) {
boundParams.put(pattern.getIdentifier(),
pattern.getObjectType());
}
if (patternSource instanceof AccumulateDescr) {
AccumulateDescr accumulate = (AccumulateDescr) patternSource;
FromAccumulateCompositeFactPattern fac = new FromAccumulateCompositeFactPattern();
fac.setSourcePattern(parseBaseDescr(m,
accumulate.getInput(),
isJavaDialect,
boundParams,
dmo));
fac.setInitCode(accumulate.getInitCode());
fac.setActionCode(accumulate.getActionCode());
fac.setReverseCode(accumulate.getReverseCode());
fac.setResultCode(accumulate.getResultCode());
FactPattern factPattern = new FactPattern(pattern.getObjectType());
factPattern.setBoundName(pattern.getIdentifier());
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;
if (fields != 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;
if (conditionalDescr instanceof NotDescr) {
comp = new CompositeFactPattern(CompositeFactPattern.COMPOSITE_TYPE_NOT);
} else if (conditionalDescr instanceof OrDescr) {
comp = new CompositeFactPattern(CompositeFactPattern.COMPOSITE_TYPE_OR);
} else if (conditionalDescr instanceof ExistsDescr) {
comp = new CompositeFactPattern(CompositeFactPattern.COMPOSITE_TYPE_EXISTS);
} else {
throw new IllegalArgumentException("Unknown conditional descr type: " + conditionalDescr);
}
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(String expr) {
//ConnectiveConstraints are handled SimpleExpr.setOperatorAndValueOnConstraint(). Therefore we
//only need to try to find the first operator before the ConnectiveConstraint separator.
if (expr.contains("&&")) {
expr = expr.substring(0,
expr.indexOf("&&")).trim();
}
if (expr.contains("||")) {
expr = expr.substring(0,
expr.indexOf("||")).trim();
}
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,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
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++;
line = line.trim();
List<RuleModelIActionPersistenceExtension> matchingExtensions = getMatchingExtensionsForLine(line,
extensions);
if (matchingExtensions.isEmpty()) {
// Continue with hardcoded parsers
} else if (matchingExtensions.size() > 1) {
throw new RuleModelDRLPersistenceException("Ambiguous RuleModelIActionPersistenceExtension implementations (" + matchingExtensions + ") found for line " + line);
} else {
m.addRhsItem(matchingExtensions.get(0).unmarshal(line));
continue;
}
if (expandedDRLInfo.hasDsl) {
String dslLine = expandedDRLInfo.dslStatementsInRhs.get(lineCounter);
while (dslLine != null) {
List<RuleModelIActionPersistenceExtension> matchingExtensionsDslLine = getMatchingExtensionsForLine(dslLine,
extensions);
if (matchingExtensionsDslLine.isEmpty()) {
m.addRhsItem(toDSLSentence(expandedDRLInfo.rhsDslPatterns,
dslLine));
} else if (matchingExtensionsDslLine.size() > 1) {
throw new RuleModelDRLPersistenceException("Ambiguous RuleModelIActionPersistenceExtension implementations (" + matchingExtensionsDslLine + ") found for line " + line);
} else {
m.addRhsItem(matchingExtensionsDslLine.get(0).unmarshal(dslLine));
}
dslLine = expandedDRLInfo.dslStatementsInRhs.get(++lineCounter);
}
}
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,
modifiedVariable,
boundParams,
dmo,
m,
isJavaDialect);
modifiedVariable = null;
modifiers = null;
}
} else if (line.startsWith("insertLogical")) {
String fact = unwrapParenthesis(line);
String type = getStatementType(fact,
factsType);
if (type != null) {
boundParams.put(fact,
type);
ActionInsertLogicalFact action = new ActionInsertLogicalFact(type);
m.addRhsItem(action);
if (factsType.containsKey(fact)) {
action.setBoundName(fact);
addSettersToAction(setStatements,
fact,
action,
boundParams,
dmo,
m,
isJavaDialect);
}
}
} else if (line.startsWith("insert")) {
String fact = unwrapParenthesis(line);
String type = getStatementType(fact,
factsType);
if (type != null) {
boundParams.put(fact,
type);
ActionInsertFact action = new ActionInsertFact(type);
m.addRhsItem(action);
if (factsType.containsKey(fact)) {
action.setBoundName(fact);
addSettersToAction(setStatements,
fact,
action,
boundParams,
dmo,
m,
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,
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,
variable,
boundParams,
dmo,
m,
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 (boundParams.containsKey(variable) || factsType.containsKey(variable) || expandedDRLInfo.hasGlobal(variable)) {
if (isJavaIdentifier(variable)) {
String methodName = line.substring(dotPos + 1,
argStart).trim();
if (isJavaIdentifier(methodName)) {
if (getSettedField(m,
methodName,
boundParams.get(variable),
dmo) != null) {
List<String> setters = setStatements.get(variable);
if (setters == null) {
setters = new ArrayList<String>();
setStatements.put(variable,
setters);
}
if (!setStatementsPosition.containsKey(variable)) {
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);
}
}
}
//The "setStatements" variable, at this point, contains a record of unmatched "set" calls. Unmatched means that they do not
//have a relationship with a resolved "insert", "insertLogical", "update" or "modify" action. Resolved means the action had been
//identified as an explicit operation above; normally where the RHS line began with such a call. Therefore it is likely the
// variable they are modifying was recorded as Free Format DRL and hence the "sets" need to be Free Format DRL too.
for (Map.Entry<String, List<String>> entry : setStatements.entrySet()) {
if (boundParams.containsKey(entry.getKey())) {
ActionSetField action = new ActionSetField(entry.getKey());
addSettersToAction(entry.getValue(),
action,
entry.getKey(),
boundParams,
dmo,
m,
isJavaDialect);
m.addRhsItem(action,
setStatementsPosition.get(entry.getKey()));
} else {
FreeFormLine action = new FreeFormLine();
StringBuilder sb = new StringBuilder();
for (String setter : entry.getValue()) {
sb.append(setter).append("\n");
}
action.setText(sb.toString());
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 List<RuleModelIActionPersistenceExtension> getMatchingExtensionsForLine(final String line,
final Collection<RuleModelIActionPersistenceExtension> extensions) {
return extensions.stream().filter(e -> e.accept(line)).collect(Collectors.toList());
}
private IAction getActionCallMethod(final RuleModel model,
final boolean isJavaDialect,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final String line,
final String variable,
final String methodName) {
final ActionCallMethodBuilder builder = new ActionCallMethodBuilder(model,
dmo,
isJavaDialect,
boundParams);
if (!builder.supports(line)) {
final FreeFormLine ffl = new FreeFormLine();
ffl.setText(line);
return ffl;
}
return builder.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<SimpleDSLSentence> simpleDslSentences,
final String dslLine) {
DSLSentence dslSentence = new DSLSentence();
for (SimpleDSLSentence simpleDslSentence : simpleDslSentences) {
String dslPattern = simpleDslSentence.getDsl();
// 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.setDrl(simpleDslSentence.getDrl());
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 RuleModel model,
final boolean isJavaDialect) {
addSettersToAction(setStatements.remove(variable),
action,
variable,
boundParams,
dmo,
model,
isJavaDialect);
}
private void addSettersToAction(final List<String> setters,
final ActionFieldList action,
final String variable,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final RuleModel model,
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,
variable,
boundParams,
dmo,
model,
isJavaDialect,
statement,
methodName);
}
}
}
private void addModifiersToAction(final String modifiers,
final ActionFieldList action,
final String variable,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final RuleModel model,
final boolean isJavaDialect) {
for (String statement : splitArgumentsList(modifiers)) {
int argStart = statement.indexOf('(');
String methodName = statement.substring(0,
argStart).trim();
addSetterToAction(action,
variable,
boundParams,
dmo,
model,
isJavaDialect,
statement,
methodName);
}
}
private void addSetterToAction(final ActionFieldList action,
final String variable,
final Map<String, String> boundParams,
final PackageDataModelOracle dmo,
final RuleModel model,
final boolean isJavaDialect,
final String statement,
final String methodName) {
String field = getSettedField(model,
methodName,
boundParams.get(variable),
dmo);
String value = unwrapParenthesis(statement);
String dataType = inferDataType(action,
field,
boundParams,
dmo,
model.getImports());
if (dataType == null) {
dataType = inferDataType(value,
boundParams,
isJavaDialect);
}
action.addFieldValue(buildFieldValue(isJavaDialect,
field,
value,
dataType,
boundParams));
}
private ActionFieldValue buildFieldValue(final boolean isJavaDialect,
String field,
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());
}
}
value = removeNumericSuffix(value,
dataType);
final int fieldNature = inferFieldNature(dataType,
value,
boundParams,
isJavaDialect);
//If the field is a formula don't adjust the param value
String paramValue = value;
switch (fieldNature) {
case FieldNatureType.TYPE_FORMULA:
break;
case FieldNatureType.TYPE_VARIABLE:
paramValue = "=" + paramValue;
break;
case FieldNatureType.TYPE_TEMPLATE:
paramValue = unwrapTemplateKey(value);
break;
default:
paramValue = adjustParam(dataType,
value,
boundParams,
isJavaDialect);
}
ActionFieldValue fieldValue = new ActionFieldValue(field,
paramValue,
dataType);
fieldValue.setNature(fieldNature);
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 RuleModel model,
final String methodName,
final String variableType,
final PackageDataModelOracle dmo) {
//Check if method is MethodInformation as multiple parameter "setters" are handled as methods and not field mutators
List<MethodInfo> mis = RuleModelPersistenceHelper.getMethodInfosForType(model,
dmo,
variableType);
if (mis != null) {
for (MethodInfo mi : mis) {
if (mi.getName().equals(methodName)) {
return null;
}
}
}
//Check if method is a field mutator
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) {
if (isSingleEval(expr)) {
return new EvalExpr(unwrapParenthesis(expr));
}
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 (isSingleEval(singleExpr)) {
return new EvalExpr(unwrapParenthesis(singleExpr));
} else {
return new SimpleExpr(singleExpr,
isJavaDialect,
boundParams,
dmo);
}
}
if (isCompositeFieldConstraint(splittedExpr)) {
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;
} else {
//The expression parts cannot be represented by a CompositeFieldConstraint as it
//contains different operators in between the component parts. Therefore we have
//to revert to simple unmarshalling of the rule.
throw new RuleModelUnmarshallingException();
}
}
private boolean isCompositeFieldConstraint(final List<String> splittedExpr) {
if (splittedExpr.size() < 2) {
return false;
}
String operator = splittedExpr.get(1);
for (int i = 1; i < splittedExpr.size(); i += 2) {
if (!splittedExpr.get(i).equals(operator)) {
return false;
}
}
return true;
}
private boolean isSingleEval(final String expr) {
if (!expr.startsWith("eval(")) {
return false;
}
int nestingLevel = 0;
final char[] characters = expr.substring(4).trim().toCharArray();
for (int i = 0; i < characters.length; i++) {
final char ch = characters[i];
if (ch == '(') {
nestingLevel++;
} else if (ch == ')') {
nestingLevel--;
if (nestingLevel == 0) {
if (i < characters.length - 1) {
return false;
}
}
}
}
return true;
}
private enum SplitterState {
START,
EXPR,
PIPE,
OR,
AMPERSAND,
AND,
NESTED,
EVAL
}
private List<String> splitExpression(final String expr) {
List<String> splittedExpr = new ArrayList<String>();
int nestingLevel = 0;
int evalNestingLevel = nestingLevel;
SplitterState status = SplitterState.START;
StringBuilder sb = new StringBuilder();
for (char ch : expr.toCharArray()) {
switch (status) {
case START:
if (ch == '(') {
status = SplitterState.NESTED;
sb.append(ch);
nestingLevel++;
} else {
status = SplitterState.EXPR;
sb.append(ch);
}
break;
case EXPR:
if (sb.toString().equals("eval(")) {
status = SplitterState.EVAL;
evalNestingLevel = nestingLevel;
nestingLevel++;
sb.append(ch);
} else if (ch == '|') {
status = SplitterState.PIPE;
} else if (ch == '&') {
status = SplitterState.AMPERSAND;
} else {
sb.append(ch);
}
break;
case EVAL:
if (ch == '(') {
nestingLevel++;
sb.append(ch);
} else if (ch == ')') {
nestingLevel--;
if (nestingLevel == evalNestingLevel) {
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;
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.indexOf(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 == null ? null : operator.trim(),
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(getSimpleFactType(field.getType(),
dmo));
}
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.getFactType()));
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(getExpressionPart(expressionPart,
currentFact));
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.getClassName());
}
}
}
}
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 == null ? null : connectiveOperator.trim()));
connectiveConstraints[i].setFactType(factPattern.getFactType());
connectiveConstraints[i].setFieldName(con.getFieldName());
connectiveConstraints[i].setFieldType(con.getFieldType());
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.contains("@{")) {
con.setConstraintValueType(BaseSingleFieldConstraint.TYPE_TEMPLATE);
con.setValue(unwrapTemplateKey(value));
} else 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 if (boundParams.containsKey(value)) {
con.setConstraintValueType(SingleFieldConstraint.TYPE_VARIABLE);
} else {
con.setConstraintValueType(SingleFieldConstraint.TYPE_RET_VALUE);
}
} 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);
}
}
/**
* If the bound type is not in the DMO it probably hasn't been imported.
* So we have little option than to fall back to keeping the value as Text.
*/
private static ExpressionPart getExpressionPart(String expressionPart,
ModelField currentFact) {
if (currentFact == null) {
return new ExpressionText(expressionPart);
} else {
return new ExpressionVariable(expressionPart,
currentFact.getClassName(),
currentFact.getType());
}
}
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;
}
}
private static class SimpleDSLSentence {
private String dsl;
private String drl;
private SimpleDSLSentence(final String dsl,
final String drl) {
this.dsl = dsl;
this.drl = drl;
}
private String getDsl() {
return this.dsl;
}
private String getDrl() {
return this.drl;
}
}
//Exception to indicate the DrlParser encountered a problem parsing the DRL. This fails-fast the unmarshalling.
public static class RuleModelUnmarshallingException extends RuntimeException {
}
//Simple fall-back parser of DRL
public RuleModel getSimpleRuleModel(final String drl) {
final RuleModel rm = new RuleModel();
rm.setPackageName(PackageNameParser.parsePackageName(drl));
rm.setImports(ImportsParser.parseImports(drl));
final Pattern rulePattern = Pattern.compile(".*\\s?rule\\s+(.+?)\\s+.*",
Pattern.DOTALL);
final Pattern lhsPattern = Pattern.compile(".*\\s+when\\s+(.+?)\\s+then.*",
Pattern.DOTALL);
final Pattern rhsPattern = Pattern.compile(".*\\s+then\\s+(.+?)\\s+end.*",
Pattern.DOTALL);
final Matcher ruleMatcher = rulePattern.matcher(drl);
if (ruleMatcher.matches()) {
String name = ruleMatcher.group(1);
if (name.startsWith("\"")) {
name = name.substring(1);
}
if (name.endsWith("\"")) {
name = name.substring(0,
name.length() - 1);
}
rm.name = name;
}
final Matcher lhsMatcher = lhsPattern.matcher(drl);
if (lhsMatcher.matches()) {
final FreeFormLine lhs = new FreeFormLine();
lhs.setText(lhsMatcher.group(1) == null ? "" : lhsMatcher.group(1).trim());
rm.addLhsItem(lhs);
}
final Matcher rhsMatcher = rhsPattern.matcher(drl);
if (rhsMatcher.matches()) {
final FreeFormLine rhs = new FreeFormLine();
rhs.setText(rhsMatcher.group(1) == null ? "" : rhsMatcher.group(1).trim());
rm.addRhsItem(rhs);
}
return rm;
}
}