Java DSL

Version 3

    java supports annotation processing at javac compile time. It allows canning of classes and of further class bytecode generation. So the user writes the class that has the representation of what we want. We scan for the classes and generate the real classess. As it uses real expressions, we can use the same bytecode in the generated classes that drools can use.

     

    main class is the rule module (group), each rule is a static nested class. Each rule has two methods "when" and "then". Note they return void, as we are just using the java syntax as representation, not execution. methods are provided for 'and', 'or', 'not', 'exists'. They return a boolean, just for the point of compiation, and take boolean vararg as arguments. Note we translate == to equals in the transformation.

     

    @RuleModule

    public class MyRuleSet {
         public static class R1 {
               public void when( A a ) {
                   and( a.getF1() > 30 );
               }
    
              public void when( A a ) {
                  println( a.getF1() );
              }
         }
    
         @Salience(100)
         public static class R2 {
              ....
         }
    }
    

     

     

    As many arguments as you want and constraints. Each argument is effectively a right input of a join.

    public static class R1 {
          public void when( A a, B b ) {
               and( a.getF1() > 30, a.getF2() == b.getF2() );
          }
    
          public void when( A a ) {
              println( a.getF1() );
          }
    } 
    

     

     

    'and' and 'not' can be used nested

    public static class R1 {
          public void when( A a, B b ) {
               and( a.getF1() > 30, not( a.getF2() == b.getF2() ) );
          }
    
          public void when( A a ) {
              println( a.getF1() );
          }
    }
    

     

    Although it may be better ot just force on a new line. I think both are valid, would be needed for regularity in sub networks.

    public static class R1 {
          public void when( A a, B b ) {
               and( a.getF1() > 30 );
               not( a.getF2() == b.getF2() );
          }
    
          public void when( A a ) {
              println( a.getF1() );
          }
    }
    

     

    supports sub networks.

    public static class R1 {
          public void when( A a, B b, C c ) {
               and( a.getF1() > 30 );
               not( and( a.getF2() == b.getF2(), c.getF1() > 40 ) );
          }
    
          public void when( A a ) {
              println( a.getF1() );
          }
    }
    

     

    'from' can be supported via the using a yeld() method - remember this is not executable it's just creating method signatures in the bytecode we can recognise, extract and transform. Notice the use of @Out for things that are created locally, that we want available in the consequence.

    public static class R1 {
        public void when(A a, B b, @Out Person p) {
            for ( Person p : a.getPeople() ) {
                yield( p );
            }
        }
    
        public void then(Person p) {
              println( p);
        }
    }
    

     

    'acc' can be supported.  It uses end() to demarcate the acc. The other option is an anonymous class, which is verbose. Without lambda's it sucks a bit, as we have to use a marker, java8 might allow us to improve this using lambda's to provide the {    } block scoping, without adding verosity.

    public static class R1 {
        public void when(A a, B b) {
            int s;
            acc( a.getF1() == "xxx")
                s = sum( p.age );
            end();
        }
    
        public void then(int s) {
              println( s);
        }
    }
    

     

     

    public static class R1 {
        public void when(A a, B b) {
            int s;
            acc( a.getF1() == "xxx")
                s = sum( p.age );
            end();
        }
    
        public void then(int s) {
              println( s);
        }
    }
    

     

     

    Any method annotated with @View generates a query. Notice while it uses 'return' I'd have preferred yield - as it's not really returning. But it was necessary to get it to compile. Notice this query does not access the WM, just the arguments and their members. But it could access WM too. We will need ot make arguments Observable, which allows the 'from' to observe changes to the object's members, without them going through the WM.

    @View
    public Employee view1(Company com) {
        for( Employee emp : com.getEmployees() {
            return emp;
        }
    }    
    

     

    For a non reactive case they can use this view like below, 'query' is our helper method

    for ( Employee : query( view1(com ) ) {
        System.out.println();
    }
    

     

     

    To get type safe results for multiple result, we can do something like below. We generate the implementation for the Result1, the vararg arguments match the position of the generated getters.

    @View
    public Result1 view1(Company com) {
        for( Employee emp : com.getEmployees() {
            return yield(emp, new SomeClass() );
        }
    }  
    

     

     

    public static interface Result1 {
    
    
        @Position(1)
        public getEmployee();
    
    
        @Position(2)    
        public getSomeClass();    
    }