Example #1
0
  protected void initDeclaredMask(BuildContext context, LeftTupleSource leftInput) {
    if (context == null || context.getLastBuiltPatterns() == null) {
      // only happens during unit tests
      leftDeclaredMask = AllSetBitMask.get();
      return;
    }

    if (leftInput.getType() != NodeTypeEnums.LeftInputAdapterNode) {
      // BetaNode's not after LIANode are not relevant for left mask property specific, so don't
      // block anything.
      leftDeclaredMask = AllSetBitMask.get();
      return;
    }

    Pattern pattern = context.getLastBuiltPatterns()[1]; // left input pattern

    ObjectType objectType =
        pattern == null || this.getType() == NodeTypeEnums.AccumulateNode
            ? ((LeftInputAdapterNode) leftInput)
                .getParentObjectSource()
                .getObjectTypeNode()
                .getObjectType()
            : pattern.getObjectType();

    if (!(objectType instanceof ClassObjectType)) {
      // Only ClassObjectType can use property specific
      leftDeclaredMask = AllSetBitMask.get();
      return;
    }

    Class objectClass = ((ClassWireable) objectType).getClassType();
    if (isPropertyReactive(context, objectClass)) {
      // TODO: at the moment if pattern is null (e.g. for eval node) we cannot calculate the mask,
      // so we leave it to 0
      if (pattern != null) {
        List<String> leftListenedProperties = pattern.getListenedProperties();
        List<String> settableProperties =
            getSettableProperties(context.getKnowledgeBase(), objectClass);
        leftDeclaredMask = calculatePositiveMask(leftListenedProperties, settableProperties);
        leftNegativeMask = calculateNegativeMask(leftListenedProperties, settableProperties);
        setLeftListenedProperties(leftListenedProperties);
      }
    } else {
      // if property specific is not on, then accept all modification propagations
      leftDeclaredMask = AllSetBitMask.get();
    }
  }
Example #2
0
  public AlphaNodeFieldConstraint getLiteralConstraint(
      final Pattern pattern,
      final String fieldName,
      final String evaluatorString,
      final String value)
      throws IntrospectionException {
    final Class<?> clazz = ((ClassObjectType) pattern.getObjectType()).getClassType();

    final InternalReadAccessor extractor =
        store.getReader(clazz, fieldName, getClass().getClassLoader());

    FieldValue fieldValue =
        FieldFactory.getInstance().getFieldValue(value, extractor.getValueType(), null);

    return new MvelConstraintTestUtil(fieldName + evaluatorString + value, fieldValue, extractor);
  }
  /* (non-Javadoc)
   * @see junit.framework.TestCase#setUp()
   */
  @Before
  public void setUp() throws Exception {
    kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase();
    listener1 = new TestRuleBaseListener("(listener-1) ");
    listener2 = new TestRuleBaseListener("(listener-2) ");
    kBase.addEventListener(listener1);
    kBase.addEventListener(listener2);

    final RuleImpl rule1 = new RuleImpl("test1");
    final ClassObjectType cheeseObjectType = new ClassObjectType(Cheese.class);
    final Pattern pattern = new Pattern(0, cheeseObjectType);

    ClassFieldAccessorStore store = new ClassFieldAccessorStore();
    store.setClassFieldAccessorCache(
        new ClassFieldAccessorCache(Thread.currentThread().getContextClassLoader()));
    store.setEagerWire(true);

    final ClassFieldReader extractor = store.getReader(Cheese.class, "type");

    final FieldValue field = FieldFactory.getInstance().getFieldValue("cheddar");

    final MvelConstraint constraint =
        new MvelConstraintTestUtil("type == \"cheddar\"", field, extractor);

    pattern.addConstraint(constraint);
    rule1.addPattern(pattern);

    rule1.setConsequence(
        new Consequence() {
          private static final long serialVersionUID = 510l;

          public void evaluate(
              final KnowledgeHelper knowledgeHelper, final WorkingMemory workingMemory)
              throws Exception {}

          public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}

          public void writeExternal(ObjectOutput out) throws IOException {}

          public String getName() {
            return "default";
          }
        });

    final RuleImpl rule2 = new RuleImpl("test2");
    final ClassObjectType cheeseObjectType2 = new ClassObjectType(Cheese.class);
    final Pattern pattern2 = new Pattern(0, cheeseObjectType2);

    final FieldValue field2 = FieldFactory.getInstance().getFieldValue("stilton");

    final MvelConstraint constraint2 =
        new MvelConstraintTestUtil("type == \"stilton\"", field, extractor);

    pattern2.addConstraint(constraint2);
    rule2.addPattern(pattern2);

    rule2.setConsequence(
        new Consequence() {
          private static final long serialVersionUID = 510l;

          public void evaluate(
              final KnowledgeHelper knowledgeHelper, final WorkingMemory workingMemory)
              throws Exception {}

          public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}

          public void writeExternal(ObjectOutput out) throws IOException {}

          public String getName() {
            return "default";
          }
        });

    pkg = new KnowledgePackageImpl("org.drools.test1");
    pkg.addRule(rule1);
    pkg.addRule(rule2);
  }
  private void processPositional(
      RuleBuildContext context,
      Query query,
      Declaration[] params,
      List<Integer> declrIndexes,
      List<Integer> varIndexes,
      List<Object> arguments,
      List<Declaration> requiredDeclarations,
      InternalReadAccessor arrayReader,
      Pattern pattern,
      BaseDescr base,
      String expression,
      ConstraintConnectiveDescr result) {
    int position = ((ExprConstraintDescr) base).getPosition();
    if (position >= arguments.size()) {
      context.addError(
          new DescrBuildError(
              context.getParentDescr(),
              base,
              null,
              "Unable to parse query '"
                  + query.getName()
                  + "', as postion "
                  + (position - 1)
                  + " for expression '"
                  + expression
                  + "' does not exist on query size "
                  + arguments.size()));
      return;
    }
    if (isVariable(expression)) {
      // is this already bound?
      Declaration declr = context.getDeclarationResolver().getDeclaration(query, expression);
      if (declr != null) {
        // it exists, so it's an input
        arguments.set(position, declr);
        declrIndexes.add(position);
        requiredDeclarations.add(declr);
      } else {
        // it doesn't exist, so it's an output
        arguments.set(position, Variable.v);

        varIndexes.add(position);

        declr = pattern.addDeclaration(expression);

        // this bit is different, notice its the ArrayElementReader that we wire up to, not the
        // declaration.
        ArrayElementReader reader =
            new ArrayElementReader(
                arrayReader, position, params[position].getExtractor().getExtractToClass());

        declr.setReadAccessor(reader);
      }
    } else {
      // it's an expression and thus an input
      MVELDumper.MVELDumperContext mvelCtx = new MVELDumper.MVELDumperContext();
      String rewrittenExpr =
          context.getCompilerFactory().getExpressionProcessor().dump(result, mvelCtx);
      arguments.set(position, MVEL.eval(rewrittenExpr)); // for now we just work with literals
    }
  }
  @SuppressWarnings("unchecked")
  private void processBinding(
      RuleBuildContext context,
      BaseDescr descr,
      Declaration[] params,
      List<Integer> declrIndexes,
      List<Integer> varIndexes,
      List<Object> arguments,
      List<Declaration> requiredDeclarations,
      InternalReadAccessor arrayReader,
      Pattern pattern,
      BindingDescr bind,
      ConstraintConnectiveDescr result) {
    Declaration declr =
        context.getDeclarationResolver().getDeclaration(context.getRule(), bind.getVariable());
    if (declr != null) {
      // check right maps to a slot, otherwise we can't reverse this and should error
      int pos = getPos(bind.getExpression(), params);
      if (pos >= 0) {
        // slot exist, reverse and continue
        String slot = bind.getExpression();
        String var = bind.getVariable();
        bind.setVariable(slot);
        bind.setExpression(var);
      } else {
        // else error, we cannot find the slot to unify against
      }
    }

    // left does not already exist, is it a slot?
    int pos = getPos(bind.getVariable(), params);
    if (pos >= 0) {
      // it's an input on a slot, is the input using bindings?
      declr =
          context.getDeclarationResolver().getDeclaration(context.getRule(), bind.getExpression());
      if (declr != null) {
        arguments.set(pos, declr);
        declrIndexes.add(pos);
        requiredDeclarations.add(declr);
      } else {
        // it must be a literal/expression
        // it's an expression and thus an input
        DrlExprParser parser = new DrlExprParser(context.getConfiguration().getLanguageLevel());
        ConstraintConnectiveDescr bresult = parser.parse(bind.getExpression());
        if (parser.hasErrors()) {
          for (DroolsParserException error : parser.getErrors()) {
            context.addError(
                new DescrBuildError(
                    context.getParentDescr(),
                    descr,
                    null,
                    "Unable to parser pattern expression:\n" + error.getMessage()));
          }
          return;
        }

        MVELDumper.MVELDumperContext mvelCtx = new MVELDumper.MVELDumperContext();
        String expr = context.getCompilerFactory().getExpressionProcessor().dump(bresult, mvelCtx);
        try {
          Object o = MVEL.eval(expr);
          arguments.set(pos, o); // for now we just work with literals
        } catch (Exception e) {
          context.addError(
              new DescrBuildError(
                  context.getParentDescr(), descr, null, "Unable to compile expression:\n" + expr));
        }
      }
    } else {
      // this is creating a new output binding
      // we know it doesn't exist, as we already checked for left == var
      declr = pattern.addDeclaration(bind.getVariable());

      pos = getPos(bind.getExpression(), params);
      if (pos < 0) {
        // error this must be a binding on a slot
        context.addError(
            new DescrBuildError(
                context.getParentDescr(),
                descr,
                null,
                "named argument does not exist:\n" + bind.getExpression()));
        return;
      }

      // this bit is different, notice its the ArrayElementReader that we wire up to, not the
      // declaration.
      ArrayElementReader reader =
          new ArrayElementReader(arrayReader, pos, params[pos].getExtractor().getExtractToClass());

      // Should the reader be registered like the others? Probably yes...
      // PatternBuilder.registerReadAccessor(  );

      declr.setReadAccessor(reader);

      varIndexes.add(pos);
      arguments.set(pos, Variable.v);
    }
  }
  private Accumulator[] buildExternalFunctions(
      final RuleBuildContext context,
      final AccumulateDescr accumDescr,
      MVELDialect dialect,
      Map<String, Declaration> decls,
      Map<String, Declaration> sourceOuterDeclr,
      BoundIdentifiers boundIds,
      boolean readLocalsFromTuple) {
    Accumulator[] accumulators;
    List<AccumulateFunctionCallDescr> functions = accumDescr.getFunctions();

    accumulators = new Accumulator[functions.size()];
    // creating the custom array reader
    InternalReadAccessor arrayReader = new SelfReferenceClassFieldReader(Object[].class, "this");

    int index = 0;
    Pattern pattern = (Pattern) context.getDeclarationResolver().peekBuildStack();
    for (AccumulateFunctionCallDescr func : functions) {
      // build an external function executor
      AccumulateFunction function =
          context.getConfiguration().getAccumulateFunction(func.getFunction());
      if (function == null) {
        // might have been imported in the package
        function =
            context
                .getKnowledgeBuilder()
                .getPackage()
                .getAccumulateFunctions()
                .get(func.getFunction());
      }
      if (function == null) {
        context.addError(
            new DescrBuildError(
                accumDescr,
                context.getRuleDescr(),
                null,
                "Unknown accumulate function: '"
                    + func.getFunction()
                    + "' on rule '"
                    + context.getRuleDescr().getName()
                    + "'. All accumulate functions must be registered before building a resource."));
        return null;
      }

      final AnalysisResult analysis =
          dialect.analyzeExpression(
              context,
              accumDescr,
              func.getParams().length > 0 ? func.getParams()[0] : "\"\"",
              boundIds);

      MVELCompilationUnit unit =
          dialect.getMVELCompilationUnit(
              func.getParams().length > 0 ? func.getParams()[0] : "\"\"",
              analysis,
              getUsedDeclarations(decls, analysis),
              getUsedDeclarations(sourceOuterDeclr, analysis),
              null,
              context,
              "drools",
              KnowledgeHelper.class,
              readLocalsFromTuple,
              MVELCompilationUnit.Scope.CONSTRAINT);

      accumulators[index] = new MVELAccumulatorFunctionExecutor(unit, function);
      // if there is a binding, create the binding
      if (func.getBind() != null) {
        if (context
            .getDeclarationResolver()
            .isDuplicated(context.getRule(), func.getBind(), function.getResultType().getName())) {
          if (!func.isUnification()) {
            context.addError(
                new DescrBuildError(
                    context.getParentDescr(),
                    accumDescr,
                    null,
                    "Duplicate declaration for variable '"
                        + func.getBind()
                        + "' in the rule '"
                        + context.getRule().getName()
                        + "'"));
          } else {
            Declaration inner =
                context.getDeclarationResolver().getDeclaration(context.getRule(), func.getBind());
            Constraint c =
                new MvelConstraint(
                    Collections.singletonList(context.getPkg().getName()),
                    accumDescr.isMultiFunction()
                        ? "this[ " + index + " ] == " + func.getBind()
                        : "this == " + func.getBind(),
                    new Declaration[] {inner},
                    null,
                    IndexUtil.ConstraintType.EQUAL,
                    context
                        .getDeclarationResolver()
                        .getDeclaration(context.getRule(), func.getBind()),
                    accumDescr.isMultiFunction()
                        ? new ArrayElementReader(arrayReader, index, function.getResultType())
                        : new SelfReferenceClassFieldReader(function.getResultType(), "this"),
                    true);
            ((MutableTypeConstraint) c).setType(Constraint.ConstraintType.BETA);
            pattern.addConstraint(c);
            index++;
          }
        } else {
          Declaration declr = pattern.addDeclaration(func.getBind());
          if (accumDescr.isMultiFunction()) {
            declr.setReadAccessor(
                new ArrayElementReader(arrayReader, index, function.getResultType()));
          } else {
            declr.setReadAccessor(
                new SelfReferenceClassFieldReader(function.getResultType(), "this"));
          }
        }
      }
      index++;
    }
    return accumulators;
  }
  @Test
  public void testSimpleExpression() throws Exception {
    PackageDescr pkgDescr = new PackageDescr("pkg1");
    KnowledgeBuilderImpl pkgBuilder = new KnowledgeBuilderImpl();
    pkgBuilder.addPackage(pkgDescr);

    InternalKnowledgePackage pkg = pkgBuilder.getPackageRegistry("pkg1").getPackage();
    final RuleDescr ruleDescr = new RuleDescr("rule 1");
    ruleDescr.setNamespace("pkg1");
    ruleDescr.setConsequence("modify (cheese) {price = 5 };\nretract (cheese)");

    DialectCompiletimeRegistry dialectRegistry =
        pkgBuilder.getPackageRegistry(pkg.getName()).getDialectCompiletimeRegistry();

    MVELDialect mvelDialect = (MVELDialect) dialectRegistry.getDialect("mvel");

    final InstrumentedBuildContent context =
        new InstrumentedBuildContent(pkgBuilder, ruleDescr, dialectRegistry, pkg, mvelDialect);

    final InstrumentedDeclarationScopeResolver declarationResolver =
        new InstrumentedDeclarationScopeResolver();

    final ObjectType cheeseObjeectType = new ClassObjectType(Cheese.class);

    final Pattern pattern = new Pattern(0, cheeseObjeectType, "cheese");

    final GroupElement subrule = new GroupElement(GroupElement.AND);
    subrule.addChild(pattern);
    final Map<String, Declaration> map = new HashMap<String, Declaration>();
    map.put("cheese", pattern.getDeclaration());
    declarationResolver.setDeclarations(map);
    context.setDeclarationResolver(declarationResolver);

    final MVELConsequenceBuilder builder = new MVELConsequenceBuilder();
    builder.build(context, RuleImpl.DEFAULT_CONSEQUENCE_NAME);

    InternalKnowledgeBase kBase = (InternalKnowledgeBase) KnowledgeBaseFactory.newKnowledgeBase();

    PropagationContextFactory pctxFactory =
        kBase.getConfiguration().getComponentFactory().getPropagationContextFactory();
    kBase.addPackage(pkg);

    StatefulKnowledgeSessionImpl ksession =
        (StatefulKnowledgeSessionImpl) kBase.newStatefulKnowledgeSession();

    final Cheese cheddar = new Cheese("cheddar", 10);
    final InternalFactHandle f0 = (InternalFactHandle) ksession.insert(cheddar);
    final LeftTupleImpl tuple = new LeftTupleImpl(f0, null, true);
    f0.removeLeftTuple(tuple);

    final AgendaItem item =
        new AgendaItemImpl(
            0,
            tuple,
            10,
            pctxFactory.createPropagationContext(1, 1, null, tuple, null),
            new RuleTerminalNode(
                0,
                new CompositeObjectSinkAdapterTest.MockBetaNode(),
                context.getRule(),
                subrule,
                0,
                new BuildContext(kBase, null)),
            null);
    final DefaultKnowledgeHelper kbHelper = new DefaultKnowledgeHelper(ksession);
    kbHelper.setActivation(item);
    ((MVELConsequence) context.getRule().getConsequence())
        .compile(
            (MVELDialectRuntimeData)
                pkgBuilder
                    .getPackageRegistry(pkg.getName())
                    .getDialectRuntimeRegistry()
                    .getDialectData("mvel"));
    context.getRule().getConsequence().evaluate(kbHelper, ksession);

    assertEquals(5, cheddar.getPrice());
  }