Package org.drools.guvnor.models.commons.backend.rule

Source Code of org.drools.guvnor.models.commons.backend.rule.BRDRLPersistence$EvalExpr

/*
* Copyright 2012 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.drools.guvnor.models.commons.backend.rule;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.drools.compiler.compiler.DrlParser;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.lang.descr.AccumulateDescr;
import org.drools.compiler.lang.descr.AndDescr;
import org.drools.compiler.lang.descr.AttributeDescr;
import org.drools.compiler.lang.descr.BaseDescr;
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.ExprConstraintDescr;
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.util.ReflectiveVisitor;
import org.drools.guvnor.models.commons.backend.imports.ImportsParser;
import org.drools.guvnor.models.commons.backend.imports.ImportsWriter;
import org.drools.guvnor.models.commons.backend.packages.PackageNameParser;
import org.drools.guvnor.models.commons.backend.packages.PackageNameWriter;
import org.drools.guvnor.models.commons.shared.imports.Import;
import org.drools.guvnor.models.commons.shared.imports.Imports;
import org.drools.guvnor.models.commons.shared.oracle.DataType;
import org.drools.guvnor.models.commons.shared.oracle.OperatorsOracle;
import org.drools.guvnor.models.commons.shared.rule.ActionCallMethod;
import org.drools.guvnor.models.commons.shared.rule.ActionExecuteWorkItem;
import org.drools.guvnor.models.commons.shared.rule.ActionFieldFunction;
import org.drools.guvnor.models.commons.shared.rule.ActionFieldList;
import org.drools.guvnor.models.commons.shared.rule.ActionFieldValue;
import org.drools.guvnor.models.commons.shared.rule.ActionGlobalCollectionAdd;
import org.drools.guvnor.models.commons.shared.rule.ActionInsertFact;
import org.drools.guvnor.models.commons.shared.rule.ActionInsertLogicalFact;
import org.drools.guvnor.models.commons.shared.rule.ActionRetractFact;
import org.drools.guvnor.models.commons.shared.rule.ActionSetField;
import org.drools.guvnor.models.commons.shared.rule.ActionUpdateField;
import org.drools.guvnor.models.commons.shared.rule.ActionWorkItemFieldValue;
import org.drools.guvnor.models.commons.shared.rule.BaseSingleFieldConstraint;
import org.drools.guvnor.models.commons.shared.rule.CEPWindow;
import org.drools.guvnor.models.commons.shared.rule.CompositeFactPattern;
import org.drools.guvnor.models.commons.shared.rule.CompositeFieldConstraint;
import org.drools.guvnor.models.commons.shared.rule.ConnectiveConstraint;
import org.drools.guvnor.models.commons.shared.rule.DSLSentence;
import org.drools.guvnor.models.commons.shared.rule.ExpressionField;
import org.drools.guvnor.models.commons.shared.rule.ExpressionFormLine;
import org.drools.guvnor.models.commons.shared.rule.ExpressionUnboundFact;
import org.drools.guvnor.models.commons.shared.rule.FactPattern;
import org.drools.guvnor.models.commons.shared.rule.FieldConstraint;
import org.drools.guvnor.models.commons.shared.rule.FieldNature;
import org.drools.guvnor.models.commons.shared.rule.FieldNatureType;
import org.drools.guvnor.models.commons.shared.rule.FreeFormLine;
import org.drools.guvnor.models.commons.shared.rule.FromAccumulateCompositeFactPattern;
import org.drools.guvnor.models.commons.shared.rule.FromCollectCompositeFactPattern;
import org.drools.guvnor.models.commons.shared.rule.FromCompositeFactPattern;
import org.drools.guvnor.models.commons.shared.rule.FromEntryPointFactPattern;
import org.drools.guvnor.models.commons.shared.rule.HasParameterizedOperator;
import org.drools.guvnor.models.commons.shared.rule.IAction;
import org.drools.guvnor.models.commons.shared.rule.IFactPattern;
import org.drools.guvnor.models.commons.shared.rule.IPattern;
import org.drools.guvnor.models.commons.shared.rule.RuleAttribute;
import org.drools.guvnor.models.commons.shared.rule.RuleModel;
import org.drools.guvnor.models.commons.shared.rule.SingleFieldConstraint;
import org.drools.guvnor.models.commons.shared.rule.SingleFieldConstraintEBLeftSide;
import org.drools.guvnor.models.commons.shared.workitems.HasBinding;
import org.drools.guvnor.models.commons.shared.workitems.PortableBooleanParameterDefinition;
import org.drools.guvnor.models.commons.shared.workitems.PortableFloatParameterDefinition;
import org.drools.guvnor.models.commons.shared.workitems.PortableIntegerParameterDefinition;
import org.drools.guvnor.models.commons.shared.workitems.PortableObjectParameterDefinition;
import org.drools.guvnor.models.commons.shared.workitems.PortableParameterDefinition;
import org.drools.guvnor.models.commons.shared.workitems.PortableStringParameterDefinition;
import org.drools.guvnor.models.commons.shared.workitems.PortableWorkDefinition;

/**
* This class persists the rule model to DRL and back
*/
public class BRDRLPersistence
        implements
        BRLPersistence {

    private static final String WORKITEM_PREFIX = "wi";

    private static final BRLPersistence INSTANCE = new BRDRLPersistence();

    public static final String DEFAULT_DIALECT = "mvel";

    //This is the default dialect for rules not specifying one explicitly
    protected DRLConstraintValueBuilder constraintValueBuilder = DRLConstraintValueBuilder.getBuilder( 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 BRDRLPersistence() {
    }

    public static BRLPersistence getInstance() {
        return INSTANCE;
    }

    /*
     * (non-Javadoc)
     * @see
     * org.drools.ide.common.server.util.BRLPersistence#marshal(org.drools.guvnor
     * .client.modeldriven.brl.RuleModel)
     */
    public String marshal( final RuleModel model ) {
        return marshalRule( model );
    }

    protected String marshalRule( final RuleModel model ) {
        boolean isDSLEnhanced = model.hasDSLSentences();
        bindingsPatterns = new HashMap<String, IFactPattern>();
        bindingsFields = new HashMap<String, FieldConstraint>();

        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 );
        buf.append( "\tthen\n" );
        this.marshalRHS( buf,
                         model,
                         isDSLEnhanced );
        this.marshalFooter( buf );
        return buf.toString();
    }

    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",
                                                    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 ) {
        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 );
            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 ) {
        return new LHSPatternVisitor( isDSLEnhanced,
                                      bindingsPatterns,
                                      bindingsFields,
                                      constraintValueBuilder,
                                      buf,
                                      nestedIndentation,
                                      isNegated );
    }

    protected void marshalRHS( final StringBuilder buf,
                               final RuleModel model,
                               final boolean isDSLEnhanced ) {
        String indentation = "\t\t";
        if ( model.rhs != null ) {

            //Add boiler-plate for actions operating on Dates
            Map<String, List<ActionFieldValue>> classes = getRHSClassDependencies( model );
            if ( classes.containsKey( DataType.TYPE_DATE ) ) {
                buf.append( indentation );
                buf.append( "java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(\"" + System.getProperty( "drools.dateformat" ) + "\");\n" );
            }

            //Add boiler-plate for actions operating on WorkItems
            if ( !getRHSWorkItemDependencies( model ).isEmpty() ) {
                buf.append( indentation );
                buf.append( "org.kie.internal.process.instance.WorkItemManager wim = (org.kie.internal.process.instance.WorkItemManager) drools.getWorkingMemory().getWorkItemManager();\n" );
            }

            //Marshall the model itself
            RHSActionVisitor actionVisitor = getRHSActionVisitor( isDSLEnhanced,
                                                                  buf,
                                                                  indentation );
            for ( IAction action : model.rhs ) {
                actionVisitor.visit( action );
            }
        }
    }

    protected RHSActionVisitor getRHSActionVisitor( final boolean isDSLEnhanced,
                                                    final StringBuilder buf,
                                                    final String indentation ) {
        return new RHSActionVisitor( isDSLEnhanced,
                                     bindingsPatterns,
                                     bindingsFields,
                                     constraintValueBuilder,
                                     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 {

        private StringBuilder buf;
        private boolean isDSLEnhanced;
        private boolean isPatternNegated;
        private String indentation;
        private Map<String, IFactPattern> bindingsPatterns;
        private Map<String, FieldConstraint> bindingsFields;
        protected DRLConstraintValueBuilder constraintValueBuilder;

        public LHSPatternVisitor( final boolean isDSLEnhanced,
                                  final Map<String, IFactPattern> bindingsPatterns,
                                  final Map<String, FieldConstraint> bindingsFields,
                                  final DRLConstraintValueBuilder constraintValueBuilder,
                                  final StringBuilder b,
                                  final String indentation,
                                  final boolean isPatternNegated ) {
            this.isDSLEnhanced = isDSLEnhanced;
            this.bindingsPatterns = bindingsPatterns;
            this.bindingsFields = bindingsFields;
            this.constraintValueBuilder = constraintValueBuilder;
            this.indentation = indentation;
            this.isPatternNegated = isPatternNegated;
            buf = b;
        }

        public void visitFactPattern( final FactPattern pattern ) {
            buf.append( indentation );
            if ( isDSLEnhanced ) {
                // adding passthrough markup
                buf.append( ">" );
            }
            generateFactPattern( pattern );
            if ( isPatternNegated ) {
                buf.append( " and " );
            }
            buf.append( "\n" );
        }

        public void visitFreeFormLine( final FreeFormLine ffl ) {
            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 ) {
                generateFactPattern( pattern.getFactPattern() );
            }
            buf.append( " from " );
            renderExpression( pattern.getExpression() );
            buf.append( "\n" );
        }

        public void visitFromCollectCompositeFactPattern( final FromCollectCompositeFactPattern pattern ) {
            visitFromCollectCompositeFactPattern( pattern,
                                                  false );
        }

        public void visitFromCollectCompositeFactPattern( final FromCollectCompositeFactPattern pattern,
                                                          final boolean isSubPattern ) {
            buf.append( indentation );
            if ( !isSubPattern && isDSLEnhanced ) {
                // adding passthrough markup
                buf.append( ">" );
            }
            if ( pattern.getFactPattern() != null ) {
                generateFactPattern( pattern.getFactPattern() );
            }
            buf.append( " from collect ( " );
            if ( pattern.getRightPattern() != null ) {
                if ( pattern.getRightPattern() instanceof FactPattern ) {
                    generateFactPattern( (FactPattern) pattern.getRightPattern() );
                } 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 ) {
                generateFactPattern( pattern.getFactPattern() );
            }
            buf.append( " from accumulate ( " );
            if ( pattern.getSourcePattern() != null ) {
                if ( pattern.getSourcePattern() instanceof FactPattern ) {
                    generateFactPattern( (FactPattern) pattern.getSourcePattern() );
                } 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 ) {
                generateFactPattern( pattern.getFactPattern() );
            }
            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 ) {
                this.generateFactPattern( (FactPattern) subPattern );
            } 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 ) {
            buf.append( expression.getText() );
        }

        public void visitDSLSentence( final DSLSentence sentence ) {
            buf.append( indentation );
            buf.append( sentence.interpolate() );
            buf.append( "\n" );
        }

        private void generateFactPattern( final FactPattern pattern ) {
            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 );
            }
            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 ) {
            int printedCount = 0;
            for ( int i = 0; i < pattern.getFieldConstraints().length; i++ ) {
                StringBuilder buffer = new StringBuilder();
                generateConstraint( pattern.getConstraintList().getConstraints()[ i ],
                                    false,
                                    buffer );
                if ( buffer.length() > 0 ) {
                    if ( printedCount > 0 ) {
                        buf.append( ", " );
                    }
                    buf.append( buffer );
                    printedCount++;
                }
            }
        }

        /**
         * 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.
         */
        private void generateConstraint( final FieldConstraint con,
                                         final boolean nested,
                                         final StringBuilder buf ) {
            if ( con instanceof CompositeFieldConstraint ) {
                CompositeFieldConstraint cfc = (CompositeFieldConstraint) con;
                if ( nested ) {
                    buf.append( "( " );
                }
                FieldConstraint[] nestedConstraints = cfc.getConstraints();
                if ( nestedConstraints != null ) {
                    for ( int i = 0; i < nestedConstraints.length; i++ ) {
                        generateConstraint( nestedConstraints[ i ],
                                            true,
                                            buf );
                        if ( i < ( nestedConstraints.length - 1 ) ) {
                            // buf.append(" ) ");
                            buf.append( cfc.getCompositeJunctionType() + " " );
                            // buf.append(" ( ");
                        }
                    }
                }
                if ( nested ) {
                    buf.append( ")" );
                }
            } else {
                generateSingleFieldConstraint( (SingleFieldConstraint) con,
                                               buf );
            }
        }

        private void generateSingleFieldConstraint( final SingleFieldConstraint constr,
                                                    final StringBuilder buf ) {
            if ( constr.getConstraintValueType() == BaseSingleFieldConstraint.TYPE_PREDICATE ) {
                buf.append( "eval( " );
                buf.append( constr.getValue() );
                buf.append( " )" );
            } else {
                if ( constr.isBound() ) {
                    bindingsFields.put( constr.getFieldBinding(),
                                        constr );
                    buf.append( constr.getFieldBinding() );
                    buf.append( " : " );
                }
                if ( ( constr.getOperator() != null
                        && ( constr.getValue() != null
                        || constr.getOperator().equals( "== null" )
                        || constr.getOperator().equals( "!= null" ) ) )
                        || constr.getFieldBinding() != null
                        || constr.getConstraintValueType() == BaseSingleFieldConstraint.TYPE_EXPR_BUILDER_VALUE
                        || constr instanceof SingleFieldConstraintEBLeftSide ) {
                    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 );
                    if ( constr instanceof SingleFieldConstraintEBLeftSide ) {
                        buf.append( ( (SingleFieldConstraintEBLeftSide) constr ).getExpressionLeftSide().getText() );
                    } else {
                        String fieldName = constr.getFieldName();
                        buf.append( fieldName );
                    }
                }

                Map<String, String> parameters = null;
                if ( constr instanceof HasParameterizedOperator ) {
                    HasParameterizedOperator hop = constr;
                    parameters = hop.getParameters();
                }

                if ( constr instanceof SingleFieldConstraintEBLeftSide ) {
                    SingleFieldConstraintEBLeftSide sfexp = (SingleFieldConstraintEBLeftSide) constr;
                    addFieldRestriction( buf,
                                         sfexp.getConstraintValueType(),
                                         sfexp.getExpressionLeftSide().getGenericType(),
                                         sfexp.getOperator(),
                                         parameters,
                                         sfexp.getValue(),
                                         sfexp.getExpressionValue() );
                } else {
                    addFieldRestriction( buf,
                                         constr.getConstraintValueType(),
                                         constr.getFieldType(),
                                         constr.getOperator(),
                                         parameters,
                                         constr.getValue(),
                                         constr.getExpressionValue() );
                }

                // and now do the connectives.
                if ( constr.getConnectives() != null ) {
                    for ( int j = 0; j < constr.getConnectives().length; j++ ) {
                        final ConnectiveConstraint conn = constr.getConnectives()[ j ];

                        parameters = null;
                        if ( conn instanceof HasParameterizedOperator ) {
                            HasParameterizedOperator hop = (HasParameterizedOperator) conn;
                            parameters = hop.getParameters();
                        }

                        addFieldRestriction( buf,
                                             conn.getConstraintValueType(),
                                             conn.getFieldType(),
                                             conn.getOperator(),
                                             parameters,
                                             conn.getValue(),
                                             conn.getExpressionValue() );
                    }
                }

            }
        }

        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 ) {
            if ( operator == null ) {
                return;
            }

            buf.append( " " );
            buf.append( operator );

            if ( parameters != null && parameters.size() > 0 ) {
                buf.append( buildOperatorParameterDRL( parameters ) );
            }

            switch ( type ) {
                case BaseSingleFieldConstraint.TYPE_RET_VALUE:
                    buildReturnValueFieldValue( value,
                                                buf );
                    break;
                case BaseSingleFieldConstraint.TYPE_LITERAL:
                    buildLiteralFieldValue( operator,
                                            type,
                                            fieldType,
                                            value,
                                            buf );
                    break;
                case BaseSingleFieldConstraint.TYPE_EXPR_BUILDER_VALUE:
                    buildExpressionFieldValue( expression,
                                               buf );
                    break;
                case BaseSingleFieldConstraint.TYPE_TEMPLATE:
                    buildTemplateFieldValue( type,
                                             fieldType,
                                             value,
                                             buf );
                    break;
                case BaseSingleFieldConstraint.TYPE_ENUM:
                    buildEnumFieldValue( operator,
                                         type,
                                         fieldType,
                                         value,
                                         buf );
                    break;
                default:
                    buildDefaultFieldValue( operator,
                                            value,
                                            buf );
            }
        }

        protected void buildReturnValueFieldValue( final String value,
                                                   final StringBuilder buf ) {
            buf.append( " " );
            buf.append( "( " );
            buf.append( value );
            buf.append( " )" );
            buf.append( " " );
        }

        protected StringBuilder buildOperatorParameterDRL( final Map<String, String> parameters ) {
            String className = parameters.get( SharedConstants.OPERATOR_PARAMETER_GENERATOR );
            if ( className == null ) {
                throw new IllegalStateException( "Implementation of 'org.kie.guvnor.guided.server.util.OperatorParameterDRLBuilder' undefined. Unable to build Operator Parameter DRL." );
            }

            try {
                OperatorParameterDRLBuilder builder = (OperatorParameterDRLBuilder) Class.forName( className ).newInstance();
                return builder.buildDRL( parameters );
            } catch ( ClassNotFoundException cnfe ) {
                throw new IllegalStateException( "Unable to generate Operator DRL using class '" + className + "'.",
                                                 cnfe );
            } catch ( IllegalAccessException iae ) {
                throw new IllegalStateException( "Unable to generate Operator DRL using class '" + className + "'.",
                                                 iae );
            } catch ( InstantiationException ie ) {
                throw new IllegalStateException( "Unable to generate Operator DRL using class '" + className + "'.",
                                                 ie );
            }

        }

        protected void buildLiteralFieldValue( final String operator,
                                               final int type,
                                               final String fieldType,
                                               final String value,
                                               final StringBuilder buf ) {
            if ( OperatorsOracle.operatorRequiresList( operator ) ) {
                populateValueList( buf,
                                   type,
                                   fieldType,
                                   value );
            } else {
                if ( !operator.equals( "== null" ) && !operator.equals( "!= null" ) ) {
                    buf.append( " " );
                    constraintValueBuilder.buildLHSFieldValue( buf,
                                                               type,
                                                               fieldType,
                                                               value );
                }
            }
            buf.append( " " );
        }

        private void populateValueList( final StringBuilder buf,
                                        final int type,
                                        final String fieldType,
                                        final String value ) {
            String workingValue = value.trim();
            if ( workingValue.startsWith( "(" ) ) {
                workingValue = workingValue.substring( 1 );
            }
            if ( workingValue.endsWith( ")" ) ) {
                workingValue = workingValue.substring( 0,
                                                       workingValue.length() - 1 );
            }
            final String[] values = workingValue.split( "," );
            buf.append( " ( " );
            for ( String v : values ) {
                v = v.trim();
                if ( v.startsWith( "\"" ) ) {
                    v = v.substring( 1 );
                }
                if ( v.endsWith( "\"" ) ) {
                    v = v.substring( 0,
                                     v.length() - 1 );
                }
                constraintValueBuilder.buildLHSFieldValue( buf,
                                                           type,
                                                           fieldType,
                                                           v );
                buf.append( ", " );
            }
            buf.delete( buf.length() - 2,
                        buf.length() );
            buf.append( " )" );
        }

        protected void buildExpressionFieldValue( final ExpressionFormLine expression,
                                                  final StringBuilder buf ) {
            if ( expression != null ) {
                buf.append( " " );
                buf.append( expression.getText() );
                buf.append( " " );
            }
        }

        protected void buildTemplateFieldValue( final int type,
                                                final String fieldType,
                                                final String value,
                                                final StringBuilder buf ) {
            buf.append( " " );
            constraintValueBuilder.buildLHSFieldValue( buf,
                                                       type,
                                                       fieldType,
                                                       "@{" + 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 {

        private StringBuilder buf;
        private boolean isDSLEnhanced;
        private String indentation;
        private int idx = 0;
        private Map<String, IFactPattern> bindingsPatterns;
        private Map<String, FieldConstraint> bindingsFields;
        protected DRLConstraintValueBuilder constraintValueBuilder;

        //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 StringBuilder b,
                                 final String indentation ) {
            this.isDSLEnhanced = isDSLEnhanced;
            this.bindingsPatterns = bindingsPatterns;
            this.bindingsFields = bindingsFields;
            this.constraintValueBuilder = constraintValueBuilder;
            this.indentation = indentation;
            this.instantiatedWorkItems = new HashSet<String>();
            buf = b;
        }

        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 ) {
            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( ">" );
            }
            if ( action.getFieldValues().length == 0 && action.getBoundName() == null ) {
                buf.append( ( isLogic ) ? "insertLogical( new " : "insert( new " );

                buf.append( action.getFactType() );
                buf.append( "() );\n" );
            } else {
                buf.append( action.getFactType() );
                if ( action.getBoundName() == null ) {
                    buf.append( " fact" );
                    buf.append( idx );
                } else {
                    buf.append( " " + action.getBoundName() );
                }
                buf.append( " = new " );
                buf.append( action.getFactType() );
                buf.append( "();\n" );
                if ( action.getBoundName() == null ) {
                    generateSetMethodCalls( "fact" + idx,
                                            action.getFieldValues() );
                } else {
                    generateSetMethodCalls( action.getBoundName(),
                                            action.getFieldValues() );
                }

                buf.append( indentation );
                if ( isDSLEnhanced ) {
                    buf.append( ">" );
                }
                if ( isLogic ) {
                    buf.append( "insertLogical( " );
                    if ( action.getBoundName() == null ) {
                        buf.append( "fact" );
                        buf.append( idx++ );
                    } else {
                        buf.append( action.getBoundName() );
                    }
                    buf.append( " );\n" );
                } else {
                    buf.append( "insert( " );
                    if ( action.getBoundName() == null ) {
                        buf.append( "fact" );
                        buf.append( idx++ );
                    } else {
                        buf.append( action.getBoundName() );
                    }

                    buf.append( " );\n" );
                }
                //                buf.append(idx++);
                //                buf.append(" );\n");
            }
        }

        public void visitActionUpdateField( final ActionUpdateField action ) {
            this.visitActionSetField( action );
            buf.append( indentation );
            if ( isDSLEnhanced ) {
                buf.append( ">" );
            }
            buf.append( "update( " );
            buf.append( action.getVariable() );
            buf.append( " );\n" );
        }

        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.kie.internal.process.instance.impl.WorkItemImpl " );
            buf.append( wiImplName );
            buf.append( " = new org.kie.internal.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++ ) {
                buf.append( indentation );
                if ( isDSLEnhanced ) {
                    buf.append( ">" );
                }
                buf.append( variableName );

                ActionFieldValue fieldValue = fieldValues[ i ];
                if ( fieldValue instanceof ActionFieldFunction ) {
                    buf.append( "." );
                    buf.append( fieldValue.getField() );
                } else {
                    buf.append( ".set" );
                    buf.append( Character.toUpperCase( fieldValues[ i ].getField().charAt( 0 ) ) );
                    buf.append( fieldValues[ i ].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_TEMPLATE ) {
                buildTemplateFieldValue( fieldValue,
                                         buf );
            } else if ( fieldValue instanceof ActionWorkItemFieldValue ) {
                buildWorkItemFieldValue( (ActionWorkItemFieldValue) fieldValue,
                                         buf );
            } else {
                buildDefaultFieldValue( fieldValue,
                                        buf );
            }
        }

        protected void buildFormulaFieldValue( 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(),
                                                       "@{" + 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( ", " );
                }

                constraintValueBuilder.buildRHSFieldValue( buf,
                                                           valueFunction.getType(),
                                                           valueFunction.getValue() );
            }
            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 BRLPersistence#unmarshal(String)
     */
    public RuleModel unmarshal( String str ) {
        return getRuleModel( preprocessDRL(str) );
    }

    public RuleModel unmarshalUsingDSL( final String str,
                                        final List<String> globals,
                                        final String... dsls ) {
        return getRuleModel( parseDSLs( preprocessDRL(str), dsls ).registerGlobals( globals ) );
    }

    private ExpandedDRLInfo parseDSLs( ExpandedDRLInfo expandedDRLInfo,
                                       String[] dsls ) {
        for ( String dsl : dsls ) {
            for ( String line : dsl.split( "\n" ) ) {
                String dslPattern = line.trim();
                if ( dslPattern.length() > 0 ) {
                    if ( dslPattern.startsWith( "[when]" ) ) {
                        expandedDRLInfo.lhsDslPatterns.add( extractDslPattern( dslPattern.substring( "[when]".length() ) ) );
                    } else if ( dslPattern.startsWith( "[then]" ) ) {
                        expandedDRLInfo.rhsDslPatterns.add( extractDslPattern( dslPattern.substring( "[then]".length() ) ) );
                    } else if ( dslPattern.startsWith( "[]" ) ) {
                        String pattern = extractDslPattern( dslPattern.substring( "[]".length() ) );
                        expandedDRLInfo.lhsDslPatterns.add( pattern );
                        expandedDRLInfo.rhsDslPatterns.add( pattern );
                    }
                }
            }
        }
        return expandedDRLInfo;
    }

    private String extractDslPattern( String line ) {
        return line.substring( 0, line.indexOf( '=' ) ).trim();
    }

    private RuleModel getRuleModel( ExpandedDRLInfo expandedDRLInfo ) {
        //De-serialize model
        RuleDescr ruleDescr = parseDrl( expandedDRLInfo );
        RuleModel model = new RuleModel();
        model.name = ruleDescr.getName();

        //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(),
                                                    expandedDRLInfo );
        parseRhs( model,
                  expandedDRLInfo.consequence != null ? expandedDRLInfo.consequence : (String)ruleDescr.getConsequence(),
                  isJavaDialect,
                  boundParams,
                  expandedDRLInfo );
        return model;
    }

    private ExpandedDRLInfo preprocessDRL(String str) {
        boolean hasDsl = false;
        StringBuilder drl = new StringBuilder();
        String thenLine = null;
        List<String> lhsStatements = new ArrayList<String>();
        List<String> rhsStatements = new ArrayList<String>();
        int statementsWithoutParanthesis = 0;

        String[] lines = str.split( "\n" );
        RuleSection ruleSection = RuleSection.HEADER;
        int lhsParenthesisBalance = 0;

        for ( String line : lines ) {
            if ( ruleSection == RuleSection.HEADER ) {
                drl.append( line ).append( "\n" );
                if ( line.contains( "when" ) ) {
                    ruleSection = RuleSection.LHS;
                }
                continue;
            }
            if ( ruleSection == RuleSection.LHS && line.contains( "then" ) ) {
                thenLine = line;
                ruleSection = RuleSection.RHS;
                continue;
            }
            if ( line.trim().startsWith( ">" ) ) {
                hasDsl = true;
            } else if ( line.indexOf( '(' ) < 0 ) {
                statementsWithoutParanthesis++;
            }
            if ( ruleSection == RuleSection.LHS ) {
                if (lhsParenthesisBalance == 0) {
                    lhsStatements.add( line );
                } else {
                    String oldLine = lhsStatements.remove(lhsStatements.size()-1);
                    lhsStatements.add( oldLine + " " + line );
                }
                lhsParenthesisBalance += paranthesisBalance(line);
            } else {
                rhsStatements.add( line );
            }
        }

        hasDsl |= statementsWithoutParanthesis == lhsStatements.size() + rhsStatements.size();

        return createExpandedDRLInfo(hasDsl, drl, thenLine, lhsStatements, rhsStatements);
    }

    private int paranthesisBalance(String str) {
        int balance = 0;
        for (char ch : str.toCharArray()) {
            if (ch == '(') {
                balance++;
            } else if (ch == ')') {
                balance--;
            }
        }
        return balance;
    }

    private ExpandedDRLInfo createExpandedDRLInfo(boolean hasDsl, StringBuilder drl, String thenLine, List<String> lhsStatements, 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 ( trimmed.startsWith( ">" ) ) {
                drl.append( trimmed.substring( 1 ) ).append( "\n" );
            } 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 ( trimmed.startsWith( ">" ) ) {
                    drl.append( trimmed.substring( 1 ) ).append( "\n" );
                } else {
                    expandedDRLInfo.dslStatementsInRhs.put( lineCounter, trimmed );
                }
            }
        }

        expandedDRLInfo.plainDrl = drl.toString();

        return expandedDRLInfo;
    }

    private ExpandedDRLInfo processFreeFormStatement(StringBuilder drl, String thenLine, List<String> lhsStatements, List<String> rhsStatements) {
        ExpandedDRLInfo expandedDRLInfo = new ExpandedDRLInfo( false );

        int lineCounter = -1;
        for ( String statement : lhsStatements ) {
            lineCounter++;
            if (isValidLHSStatement(statement)) {
                drl.append( statement ).append( "\n" );
            } else {
                expandedDRLInfo.freeFormStatementsInLhs.put( lineCounter, statement );
            }
        }

        drl.append( thenLine ).append( "\n" );

        expandedDRLInfo.consequence = "";
        lineCounter = -1;
        for ( String statement : rhsStatements ) {
            String trimmed = statement.trim();
            if ( trimmed.endsWith("end") ) {
                trimmed = trimmed.substring(0, trimmed.length()-3);
            }
            if (trimmed.length() > 0) {
                expandedDRLInfo.consequence += (trimmed + "\n");
            }
            drl.append( statement ).append( "\n" );
        }

        expandedDRLInfo.plainDrl = drl.toString();
        return expandedDRLInfo;
    }

    private boolean isValidLHSStatement(String lhs) {
        // TODO: How to identify a non valid (free form) lhs statement?
        return lhs.indexOf('(') > 0 || lhs.indexOf(':') > 0;
    }

    private enum RuleSection {HEADER, LHS, RHS}

    private static class ExpandedDRLInfo {

        private final boolean hasDsl;
        private String plainDrl;
        private String consequence;

        private Map<Integer, String> dslStatementsInLhs;
        private Map<Integer, String> dslStatementsInRhs;

        private Map<Integer, String> freeFormStatementsInLhs;

        private List<String> lhsDslPatterns;
        private List<String> rhsDslPatterns;

        private Set<String> globals = new HashSet<String>();

        private ExpandedDRLInfo( boolean hasDsl ) {
            this.hasDsl = hasDsl;
            dslStatementsInLhs = new HashMap<Integer, String>();
            dslStatementsInRhs = new HashMap<Integer, String>();
            freeFormStatementsInLhs = new HashMap<Integer, String>();
            lhsDslPatterns = new ArrayList<String>();
            rhsDslPatterns = new ArrayList<String>();
        }

        public boolean hasGlobal( String name ) {
            return globals.contains( name );
        }

        public ExpandedDRLInfo registerGlobals( List<String> globalStatements ) {
            if ( globalStatements != null ) {
                for ( String globalStatement : globalStatements ) {
                    String identifier = getIdentifier( globalStatement );
                    if ( identifier != null ) {
                        globals.add( identifier );
                    }
                }
            }
            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( List<GlobalDescr> globalDescrs ) {
            if ( globalDescrs != null ) {
                for ( GlobalDescr globalDescr : globalDescrs ) {
                    globals.add( globalDescr.getIdentifier() );
                }
            }
            return this;
        }
    }

    private RuleDescr parseDrl( ExpandedDRLInfo expandedDRLInfo ) {
        DrlParser drlParser = new DrlParser();
        PackageDescr packageDescr = null;
        try {
            packageDescr = drlParser.parse( true, expandedDRLInfo.plainDrl );
        } catch ( DroolsParserException e ) {
            throw new RuntimeException( e );
        }
        expandedDRLInfo.registerGlobalDescrs( packageDescr.getGlobals() );
        return packageDescr.getRules().get( 0 );
    }

    private boolean parseAttributes( RuleModel m,
                                     Map<String, AttributeDescr> attributes ) {
        boolean isJavaDialect = false;
        for ( Map.Entry<String, AttributeDescr> entry : attributes.entrySet() ) {
            String name = entry.getKey();
            String value = entry.getValue().getValue();
            RuleAttribute ruleAttribute = new RuleAttribute( name, value );
            m.addAttribute( ruleAttribute );
            isJavaDialect |= name.equals( "dialect" ) && value.equals( "java" );
        }
        return isJavaDialect;
    }

    private Map<String, String> parseLhs( RuleModel m,
                                          AndDescr lhs,
                                          ExpandedDRLInfo expandedDRLInfo ) {
        Map<String, String> boundParams = new HashMap<String, String>();
        int lineCounter = -1;
        for ( BaseDescr descr : lhs.getDescrs() ) {
            lineCounter = parseNonDrlInLhs(m, expandedDRLInfo, lineCounter);
            m.addLhsItem( parseBaseDescr( descr, boundParams ) );
        }
        parseNonDrlInLhs(m, expandedDRLInfo, lineCounter);
        return boundParams;
    }

    private int parseNonDrlInLhs( RuleModel m,
                                  ExpandedDRLInfo expandedDRLInfo,
                                  int lineCounter) {
        lineCounter++;
        lineCounter = parseDslInLhs(m, expandedDRLInfo, lineCounter);
        lineCounter = parseFreeForm(m, expandedDRLInfo, lineCounter);
        return lineCounter;
    }

    private int parseDslInLhs(RuleModel m, 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(RuleModel m, 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( BaseDescr descr,
                                     Map<String, String> boundParams ) {
        if ( descr instanceof PatternDescr ) {
            return parsePatternDescr( (PatternDescr) descr, boundParams );
        } else if ( descr instanceof AndDescr ) {
            AndDescr andDescr = (AndDescr) descr;
            return parseBaseDescr( andDescr.getDescrs().get( 0 ), boundParams );
        } else if ( descr instanceof ConditionalElementDescr ) {
            return parseExistentialElementDescr( (ConditionalElementDescr) descr, boundParams );
        }
        return null;
    }

    private IFactPattern parsePatternDescr( PatternDescr pattern,
                                            Map<String, String> boundParams ) {
        if ( pattern.getSource() != null ) {
            return parsePatternSource( pattern, pattern.getSource(), boundParams );
        }
        return getFactPattern( pattern, boundParams );
    }

    private FactPattern getFactPattern( PatternDescr pattern,
                                        Map<String, String> boundParams ) {
        String type = pattern.getObjectType();
        FactPattern factPattern = new FactPattern( type );
        parseConstraint( factPattern, pattern.getConstraint() );
        if ( pattern.getIdentifier() != null ) {
            String identifier = pattern.getIdentifier();
            factPattern.setBoundName( identifier );
            boundParams.put( identifier, type );
        }
        return factPattern;
    }

    private IFactPattern parsePatternSource( PatternDescr pattern,
                                             PatternSourceDescr patternSource,
                                             Map<String, String> boundParams ) {
        if ( patternSource instanceof AccumulateDescr ) {
            AccumulateDescr accumulate = (AccumulateDescr) patternSource;
            FromAccumulateCompositeFactPattern fac = new FromAccumulateCompositeFactPattern();
            fac.setSourcePattern( parseBaseDescr( accumulate.getInput(), boundParams ) );
            fac.setFactPattern( new FactPattern( pattern.getObjectType() ) );
            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( collect.getInputPattern(), boundParams ) );
            fac.setFactPattern( new FactPattern( pattern.getObjectType() ) );
            return fac;
        } else if ( patternSource instanceof EntryPointDescr ) {
            EntryPointDescr entryPoint = (EntryPointDescr) patternSource;
            FromEntryPointFactPattern fep = new FromEntryPointFactPattern();
            fep.setEntryPointName( entryPoint.getText() );
            fep.setFactPattern( getFactPattern( pattern, boundParams ) );
            return fep;
        }
        throw new RuntimeException( "Unknown pattern source " + patternSource );
    }

    private CompositeFactPattern parseExistentialElementDescr( ConditionalElementDescr conditionalDescr,
                                                               Map<String, String> boundParams ) {
        CompositeFactPattern comp = conditionalDescr instanceof NotDescr ?
                new CompositeFactPattern( CompositeFactPattern.COMPOSITE_TYPE_NOT ) :
                conditionalDescr instanceof OrDescr ?
                        new CompositeFactPattern( CompositeFactPattern.COMPOSITE_TYPE_OR ) :
                        new CompositeFactPattern( CompositeFactPattern.COMPOSITE_TYPE_EXISTS );
        addPatternToComposite( conditionalDescr, comp, boundParams );
        return comp;
    }

    private void addPatternToComposite( ConditionalElementDescr conditionalDescr,
                                        CompositeFactPattern comp,
                                        Map<String, String> boundParams ) {
        for ( Object descr : conditionalDescr.getDescrs() ) {
            if ( descr instanceof PatternDescr ) {
                comp.addFactPattern( parsePatternDescr( (PatternDescr) descr, boundParams ) );
            } else if ( descr instanceof ConditionalElementDescr ) {
                addPatternToComposite( (ConditionalElementDescr) descr, comp, boundParams );
            }
        }
    }

    private void parseConstraint( FactPattern factPattern,
                                  ConditionalElementDescr constraint ) {
        for ( BaseDescr descr : constraint.getDescrs() ) {
            if ( descr instanceof ExprConstraintDescr ) {
                ExprConstraintDescr exprConstraint = (ExprConstraintDescr) descr;
                Expr expr = parseExpr( exprConstraint.getExpression() );
                factPattern.addConstraint( expr.asFieldConstraint( factPattern ) );
            }
        }
    }

    private static final String[] OPERATORS = new String[]{ "==", "!=", "<=", ">=", "<", ">", "not in", "in" };

    private static String findOperator( String expr ) {
        for ( String op : OPERATORS ) {
            if ( expr.contains( op ) ) {
                return op;
            }
        }
        return null;
    }

    private void parseRhs( RuleModel m,
                           String rhs,
                           boolean isJavaDialect,
                           Map<String, String> boundParams,
                           ExpandedDRLInfo expandedDRLInfo ) {
        PortableWorkDefinition pwd = null;
        Map<String, List<String>> setStatements = new HashMap<String, List<String>>();
        Map<String, String> factsType = new HashMap<String, String>();

        int lineCounter = -1;
        for ( String line : rhs.split( "\n" ) ) {
            lineCounter++;
            if ( expandedDRLInfo.hasDsl ) {
                String dslLine = expandedDRLInfo.dslStatementsInRhs.get( lineCounter );
                while ( dslLine != null ) {
                    m.addRhsItem( toDSLSentence( expandedDRLInfo.rhsDslPatterns, dslLine ) );
                    dslLine = expandedDRLInfo.dslStatementsInRhs.get( ++lineCounter );
                }
            }
            line = line.trim();
            if ( line.startsWith( "insertLogical" ) ) {
                String fact = unwrapParenthesis( line );
                String type = getStatementType( fact, factsType );
                if ( type != null ) {
                    ActionInsertLogicalFact action = new ActionInsertLogicalFact( type );
                    m.addRhsItem( action );
                    if ( factsType.containsKey( fact ) ) {
                        addSettersToAction( setStatements, fact, action, isJavaDialect );
                    }
                }
            } else if ( line.startsWith( "insert" ) ) {
                String fact = unwrapParenthesis( line );
                String type = getStatementType( fact, factsType );
                if ( type != null ) {
                    ActionInsertFact action = new ActionInsertFact( type );
                    m.addRhsItem( action );
                    if ( factsType.containsKey( fact ) ) {
                        action.setBoundName( fact );
                        addSettersToAction( setStatements, fact, action, isJavaDialect );
                    }
                }
            } else if ( line.startsWith( "update" ) ) {
                String variable = unwrapParenthesis( line );
                ActionUpdateField action = new ActionUpdateField();
                action.setVariable( variable );
                m.addRhsItem( action );
                addSettersToAction( setStatements, variable, action, isJavaDialect );
            } else if ( line.startsWith( "retract" ) ) {
                String variable = unwrapParenthesis( line );
                m.addRhsItem( new ActionRetractFact( variable ) );
            } else if ( line.startsWith( "org.kie.internal.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();
                    String methodName = line.substring( dotPos + 1, argStart ).trim();
                    if ( isJavaIdentifier( methodName ) ) {
                        if ( getSettedField( methodName ) != null ) {
                            List<String> setters = setStatements.get( variable );
                            if ( setters == null ) {
                                setters = new ArrayList<String>();
                                setStatements.put( variable, setters );
                            }
                            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 {
                            ActionCallMethod acm = new ActionCallMethod();
                            acm.setMethodName( methodName );
                            acm.setVariable( variable );
                            m.addRhsItem( acm );
                            String params = unwrapParenthesis( line );
                            for ( String param : params.split( "," ) ) {
                                param = param.trim();
                                if ( param.length() == 0 ) {
                                    continue;
                                }
                                String dataType = inferDataType( param, isJavaDialect );
                                acm.addFieldValue( new ActionFieldFunction( null, adjustParam( dataType, param, isJavaDialect ), dataType ) );
                            }
                        }
                        continue;
                    }
                }

                int eqPos = line.indexOf( '=' );
                if ( eqPos > 0 ) {
                    String field = line.substring( 0, eqPos ).trim();
                    String[] split = field.split( " " );
                    if ( split.length == 2 ) {
                        factsType.put( split[ 1 ], split[ 0 ] );
                    }
                } else if (line.trim().length() > 0) {
                    FreeFormLine ffl = new FreeFormLine();
                    ffl.setText(line);
                    m.addRhsItem( ffl );
                }
            }
        }

        for ( Map.Entry<String, List<String>> entry : setStatements.entrySet() ) {
            ActionSetField action = new ActionSetField( entry.getKey() );
            addSettersToAction( entry.getValue(), action, isJavaDialect );
            m.addRhsItem( action );
        }

        if ( expandedDRLInfo.hasDsl ) {
            String dslLine = expandedDRLInfo.dslStatementsInRhs.get( ++lineCounter );
            while ( dslLine != null ) {
                m.addRhsItem( toDSLSentence( expandedDRLInfo.rhsDslPatterns, dslLine ) );
                dslLine = expandedDRLInfo.dslStatementsInRhs.get( ++lineCounter );
            }
        }
    }

    private DSLSentence toDSLSentence( List<String> dslPatterns,
                                       String dslLine ) {
        DSLSentence dslSentence = new DSLSentence();
        for ( String dslPattern : dslPatterns ) {
            String regex = dslPattern.replaceAll( "\\{\\s*[\\:\\.\\w]+\\s*\\}", "(.*)" );
            Matcher m = Pattern.compile( regex ).matcher( dslLine );
            if ( m.matches() ) {
                dslSentence.setDefinition( dslPattern );
                for ( int i = 0; i < m.groupCount(); i++ ) {
                    dslSentence.getValues().get( i ).setValue( m.group( i + 1 ) );
                }
                return dslSentence;
            }
        }
        dslSentence.setDefinition( dslLine );
        return dslSentence;
    }

    private PortableParameterDefinition buildPortableParameterDefinition( String name,
                                                                          String value,
                                                                          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( Map<String, List<String>> setStatements,
                                     String variable,
                                     ActionFieldList action,
                                     boolean isJavaDialect ) {
        addSettersToAction( setStatements.remove( variable ), action, isJavaDialect );
    }

    private void addSettersToAction( List<String> setters,
                                     ActionFieldList action,
                                     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();
                String field = getSettedField( methodName );
                String value = unwrapParenthesis( statement );
                String dataType = inferDataType( value, isJavaDialect );
                action.addFieldValue( buildFieldValue( isJavaDialect, field, value, dataType ) );
            }
        }
    }

    private ActionFieldValue buildFieldValue( boolean isJavaDialect,
                                              String field,
                                              String value,
                                              String dataType ) {
        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() );
            }
        }
        return new ActionFieldValue( field, adjustParam( dataType, value, isJavaDialect ), dataType );
    }

    private boolean isJavaIdentifier( 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 inferDataType( String param,
                                  boolean isJavaDialect ) {
        if ( param.startsWith( "sdf.parse(\"" ) ) {
            return DataType.TYPE_DATE;
        } else if ( param.startsWith( "\"" ) ) {
            return DataType.TYPE_STRING;
        } else if ( param.equals( "true" ) || param.equals( "false" ) ) {
            return DataType.TYPE_BOOLEAN;
        } else if ( param.endsWith( "B" ) || ( isJavaDialect && param.startsWith( "new java.math.BigDecimal" ) ) ) {
            return DataType.TYPE_NUMERIC_BIGDECIMAL;
        } else if ( param.endsWith( "I" ) || ( isJavaDialect && param.startsWith( "new java.math.BigInteger" ) ) ) {
            return DataType.TYPE_NUMERIC_BIGINTEGER;
        }
        return DataType.TYPE_NUMERIC;
    }

    private String adjustParam( String dataType,
                                String param,
                                boolean isJavaDialect ) {
        if ( dataType == DataType.TYPE_DATE ) {
            return param.substring( "sdf.parse(\"".length(), param.length() - 2 );
        } else if ( dataType == DataType.TYPE_STRING ) {
            return param.substring( 1, param.length() - 1 );
        } else if ( dataType == DataType.TYPE_NUMERIC_BIGDECIMAL || dataType == DataType.TYPE_NUMERIC_BIGINTEGER ) {
            if ( isJavaDialect ) {
                return param.substring( "new java.math.BigDecimal(\"".length(), param.length() - 2 );
            } else {
                return param.substring( 0, param.length() - 1 );
            }
        }
        return param;
    }

    private String getSettedField( String methodName ) {
        if ( methodName.length() > 3 && methodName.startsWith( "set" ) ) {
            String field = methodName.substring( 3 );
            if ( Character.isUpperCase( field.charAt( 0 ) ) ) {
                return field.substring( 0, 1 ).toLowerCase() + field.substring( 1 );
            }
        }
        return null;
    }

    private static String unwrapParenthesis( String s ) {
        int start = s.indexOf( '(' );
        int end = s.lastIndexOf( ')' );
        return s.substring( start + 1, end ).trim();
    }

    private String getStatementType( String fact,
                                     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( String expr ) {
        List<String> splittedExpr = splitExpression( expr );
        if ( splittedExpr.size() == 1 ) {
            String singleExpr = splittedExpr.get( 0 );
            if ( singleExpr.startsWith( "(" ) ) {
                return parseExpr( singleExpr.substring( 1 ) );
            } else if ( singleExpr.startsWith( "eval" ) ) {
                return new EvalExpr( unwrapParenthesis( singleExpr ) );
            } else {
                return new SimpleExpr( singleExpr );
            }
        }
        ComplexExpr complexExpr = new ComplexExpr( splittedExpr.get( 1 ) );
        for ( int i = 0; i < splittedExpr.size(); i += 2 ) {
            complexExpr.subExprs.add( parseExpr( splittedExpr.get( i ) ) );
        }
        return complexExpr;
    }

    private enum SplitterState {
        START, EXPR, PIPE, OR, AMPERSAND, AND, NESTED
    }

    private List<String> splitExpression( String expr ) {
        List<String> splittedExpr = new ArrayList<String>();
        int nestingLevel = 0;
        SplitterState status = SplitterState.START;

        StringBuilder sb = new StringBuilder();
        for ( char ch : expr.toCharArray() ) {
            switch ( status ) {
                case START:
                    if ( ch == '(' ) {
                        status = SplitterState.NESTED;
                        nestingLevel++;
                    } else {
                        status = SplitterState.EXPR;
                        sb.append( ch );
                    }
                    break;
                case EXPR:
                    if ( ch == '|' ) {
                        status = SplitterState.PIPE;
                    } else if ( ch == '&' ) {
                        status = SplitterState.AMPERSAND;
                    } else {
                        sb.append( ch );
                    }
                    break;
                case PIPE:
                    if ( ch == '|' ) {
                        status = SplitterState.OR;
                    } else {
                        status = SplitterState.EXPR;
                        sb.append( '|' ).append( ch );
                    }
                    break;
                case AMPERSAND:
                    if ( ch == '&' ) {
                        status = SplitterState.AND;
                    } else {
                        status = SplitterState.EXPR;
                        sb.append( '&' ).append( ch );
                    }
                    break;
                case OR:
                case AND:
                    if ( ch == '=' || ch == '!' || ch == '<' || ch == '>' ) {
                        status = SplitterState.EXPR;
                        sb.append( status == SplitterState.AND ? "&& " : "|| " );
                        sb.append( ch );
                    } else if ( Character.isJavaIdentifierStart( ch ) ) {
                        String currentExpr = sb.toString().trim();
                        if ( currentExpr.length() > 0 ) {
                            splittedExpr.add( currentExpr );
                        }
                        splittedExpr.add( status == SplitterState.AND ? "&&" : "||" );
                        status = SplitterState.EXPR;
                        sb = new StringBuilder();
                        sb.append( ch );
                    } else if ( ch == '(' ) {
                        String currentExpr = sb.toString().trim();
                        if ( currentExpr.length() > 0 ) {
                            splittedExpr.add( currentExpr );
                        }
                        splittedExpr.add( status == SplitterState.AND ? "&&" : "||" );
                        status = SplitterState.NESTED;
                        nestingLevel++;
                        sb = new StringBuilder();
                        sb.append( ch );
                    }
                    break;
                case NESTED:
                    if ( ch == '(' ) {
                        nestingLevel++;
                        sb.append( ch );
                    } else if ( ch == ')' ) {
                        nestingLevel--;
                        if ( nestingLevel == 0 ) {
                            String currentExpr = sb.toString().trim();
                            if ( currentExpr.length() > 0 ) {
                                splittedExpr.add( "(" + currentExpr );
                            }
                            status = SplitterState.EXPR;
                            sb = new StringBuilder();
                        } else {
                            sb.append( ch );
                        }
                    } else {
                        sb.append( ch );
                    }
                    break;
            }
        }
        String currentExpr = sb.toString().trim();
        if ( currentExpr.length() > 0 ) {
            splittedExpr.add( currentExpr );
        }
        return splittedExpr;
    }

    private interface Expr {

        FieldConstraint asFieldConstraint( FactPattern factPattern );
    }

    private static class SimpleExpr implements Expr {

        private final String expr;

        private SimpleExpr( String expr ) {
            this.expr = expr;
        }

        public FieldConstraint asFieldConstraint( FactPattern factPattern ) {
            String fieldName = expr;

            String operator = findOperator( expr );
            String value = null;
            if ( operator != null ) {
                int opPos = expr.indexOf( operator );
                fieldName = expr.substring( 0, opPos ).trim();
                value = expr.substring( opPos + operator.length(), expr.length() ).trim();
            }

            return fieldName.contains( "." ) ?
                    asExpressionBuilderConstraint( factPattern, fieldName, operator, value ) :
                    asSingleFieldConstraint( fieldName, operator, value );
        }

        private FieldConstraint asSingleFieldConstraint( String fieldName,
                                                         String operator,
                                                         String value ) {
            SingleFieldConstraint con = new SingleFieldConstraint();
            fieldName = setFieldBindingOnContraint( fieldName, con );
            con.setFieldName( fieldName );
            setOperatorAndValueOnConstraint( operator, value, con );
            return con;
        }

        private FieldConstraint asExpressionBuilderConstraint( FactPattern factPattern,
                                                               String fieldName,
                                                               String operator,
                                                               String value ) {
            SingleFieldConstraintEBLeftSide con = new SingleFieldConstraintEBLeftSide();
            fieldName = setFieldBindingOnContraint( fieldName, con );
            con.getExpressionLeftSide().appendPart( new ExpressionUnboundFact( factPattern ) );

            String type = setOperatorAndValueOnConstraint( operator, value, con );
            String[] splits = fieldName.split( "\\." );
            for ( int i = 0; i < splits.length - 1; i++ ) {
                con.getExpressionLeftSide().appendPart( new ExpressionField( splits[ i ].trim(), "", DataType.TYPE_OBJECT ) );
            }
            con.getExpressionLeftSide().appendPart( new ExpressionField( splits[ splits.length - 1 ].trim(), "", type ) );
            return con;
        }

        private String setFieldBindingOnContraint( String fieldName,
                                                   SingleFieldConstraint con ) {
            int colonPos = fieldName.indexOf( ':' );
            if ( colonPos > 0 ) {
                String fieldBinding = fieldName.substring( 0, colonPos ).trim();
                con.setFieldBinding( fieldBinding );
                fieldName = fieldName.substring( colonPos + 1 ).trim();
            }
            return fieldName;
        }

        private String setOperatorAndValueOnConstraint( String operator,
                                                        String value,
                                                        SingleFieldConstraint con ) {
            if ( operator != null ) {
                con.setOperator( operator );
                boolean isAnd = value.contains( "&&" );
                String[] splittedValue = isAnd ? value.split( "\\&\\&" ) : value.split( "\\|\\|" );
                String type = setValueOnConstraint( operator, con, splittedValue[ 0 ].trim() );

                if ( splittedValue.length > 1 ) {
                    ConnectiveConstraint[] connectiveConstraints = new ConnectiveConstraint[ splittedValue.length - 1 ];
                    for ( int i = 0; i < connectiveConstraints.length; i++ ) {
                        String constraint = splittedValue[ i + 1 ].trim();
                        String connectiveOperator = findOperator( constraint );
                        String connectiveValue = constraint.substring( connectiveOperator.length() ).trim();

                        connectiveConstraints[ i ] = new ConnectiveConstraint();
                        connectiveConstraints[ i ].setOperator( ( isAnd ? "&& " : "|| " ) + connectiveOperator );
                        setValueOnConstraint( operator, connectiveConstraints[ i ], connectiveValue );
                    }
                    con.setConnectives( connectiveConstraints );
                }
                return type;
            }
            return null;
        }

        private String setValueOnConstraint( String operator,
                                             BaseSingleFieldConstraint con,
                                             String value ) {
            String type = null;
            if ( value.startsWith( "\"" ) ) {
                type = DataType.TYPE_STRING;
                con.setConstraintValueType( SingleFieldConstraint.TYPE_LITERAL );
                con.setValue( value.substring( 1, value.length() - 1 ) );
            } else if ( value.startsWith( "(" ) ) {
                if ( operator.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 {
                        con.setConstraintValueType( SingleFieldConstraint.TYPE_VARIABLE );
                    }
                } else {
                    if ( value.endsWith( "I" ) ) {
                        type = DataType.TYPE_NUMERIC_BIGINTEGER;
                        value = value.substring( 0, value.length() - 1 );
                    } else if ( value.endsWith( "B" ) ) {
                        type = DataType.TYPE_NUMERIC_BIGDECIMAL;
                        value = value.substring( 0, value.length() - 1 );
                    } else {
                        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 static class ComplexExpr implements Expr {

        private final List<Expr> subExprs = new ArrayList<Expr>();
        private final String connector;

        private ComplexExpr( String connector ) {
            this.connector = connector;
        }

        public FieldConstraint asFieldConstraint( 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( factPattern ) );
            }
            return comp;
        }
    }

    private static class EvalExpr implements Expr {

        private final String expr;

        private EvalExpr( String expr ) {
            this.expr = expr;
        }

        public FieldConstraint asFieldConstraint( FactPattern factPattern ) {
            SingleFieldConstraint con = new SingleFieldConstraint();
            con.setConstraintValueType( SingleFieldConstraint.TYPE_PREDICATE );
            con.setValue( expr );
            return con;
        }
    }
}
TOP

Related Classes of org.drools.guvnor.models.commons.backend.rule.BRDRLPersistence$EvalExpr

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.