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

package org.drools.eclipse.editors.completion;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;

import org.drools.compiler.builder.impl.KnowledgeBuilderConfigurationImpl;
import org.drools.compiler.compiler.PackageRegistry;
import org.drools.compiler.lang.Location;
import org.drools.compiler.lang.descr.GlobalDescr;
import org.drools.compiler.rule.builder.dialect.mvel.MVELConsequenceBuilder;
import org.drools.compiler.rule.builder.dialect.mvel.MVELDialect;
import org.drools.core.base.ClassTypeResolver;
import org.drools.core.rule.MVELDialectRuntimeData;
import org.drools.core.spi.KnowledgeHelper;
import org.drools.core.util.asm.ClassFieldInspector;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DRLInfo.RuleInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.DroolsPluginImages;
import org.drools.eclipse.editors.AbstractRuleEditor;
import org.drools.eclipse.util.ProjectClassLoader;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.JavaMethodCompletionProposal;
import org.eclipse.jdt.internal.ui.text.java.LazyJavaCompletionProposal;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IFileEditorInput;
import org.kie.api.runtime.rule.RuleContext;
import org.mvel2.MVEL;
import org.mvel2.ParserConfiguration;
import org.mvel2.ParserContext;
import org.mvel2.compiler.CompiledExpression;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.compiler.PropertyVerifier;

/**
 * For handling within rules.
 */
public class RuleCompletionProcessor extends DefaultCompletionProcessor {

    private static final String DIALECT     = "dialect";

    private static final Image  DROOLS_ICON = DroolsPluginImages.getImage( DroolsPluginImages.DROOLS );

    private static final Image  CLASS_ICON  = DroolsPluginImages.getImage( DroolsPluginImages.CLASS );

    /**
     * A CompletionContext contains the DRL backtext parsing results, to avoid
     * multilpe parser invocations
     */
    private CompletionContext context;

    public RuleCompletionProcessor(AbstractRuleEditor editor) {
        super( editor );
    }

    protected List<ICompletionProposal> getCompletionProposals(ITextViewer viewer, int documentOffset) {
        try {
            final List<ICompletionProposal> list = new ArrayList<ICompletionProposal>();
            IDocument doc = viewer.getDocument();

            String backText = readBackwards( documentOffset,
                                             doc );
            final String prefix = CompletionUtil.stripLastWord( backText );

            // if inside the keyword "rule ", no code completion
            if ( backText.length() < 5 ) {
                return list;
            }

            this.context = new CompletionContext( backText );
            Location location = context.getLocation();

            if ( location.getType() == Location.LOCATION_RULE_HEADER ) {
                addRuleHeaderProposals( list,
                                        documentOffset,
                                        prefix,
                                        backText );
            } else if ( location.getType() == Location.LOCATION_RHS ) {
                addRHSCompletionProposals( list,
                                           documentOffset,
                                           prefix,
                                           backText,
                                           (String) location.getProperty( Location.LOCATION_LHS_CONTENT ),
                                           (String) location.getProperty( Location.LOCATION_RHS_CONTENT ) );
            } else {
                addLHSCompletionProposals( list,
                                           documentOffset,
                                           location,
                                           prefix,
                                           backText );
            }

            filterProposalsOnPrefix(prefix, list);
            return list;
        } catch ( Throwable t ) {
            DroolsEclipsePlugin.log( t );
        }
        return null;
    }

    protected void addRHSCompletionProposals(List<ICompletionProposal> list,
                                             int documentOffset,
                                             String prefix,
                                             String backText,
                                             String conditions,
                                             String consequence) {
        // only add functions and keywords if at the beginning of a
        // new statement
        if ( consequence == null || consequence.length() < prefix.length() ) {
            // possible if doing code completion directly after "then"
            return;
        }
        String consequenceWithoutPrefix = consequence.substring( 0,
                                                                 consequence.length() - prefix.length() );

        if ( context == null ) {
            context = new CompletionContext( backText );
        }

        boolean startOfDialectExpression = CompletionUtil.isStartOfDialectExpression( consequenceWithoutPrefix );
        if ( //isJavaDialect() && 
                startOfDialectExpression ) {
            addRHSKeywordCompletionProposals( list,
                                              documentOffset,
                                              prefix );
            addRHSFunctionCompletionProposals( list,
                                               documentOffset,
                                               prefix );
        }
        

        //if we have 1st a dialect defined locally, or 2nd a global dialect
        //the locally defined dialect will override the package default
        if ( isJavaDialect() ) {
            addRHSJavaCompletionProposals( list,
                                           documentOffset,
                                           prefix,
                                           backText,
                                           consequence );
        } else if ( isMvelDialect() ) {
            addRHSMvelCompletionProposals( list,
                                           documentOffset,
                                           prefix,
                                           backText,
                                           consequence,
                                           startOfDialectExpression );
        }
    }

    private boolean isJavaDialect() {
        // java is the default dialect, so no package dialect means java
        // conditions are ordered from the more specific to the more general
        if ( context.isJavaDialect() ) {
            return true;
        } else if ( context.isDefaultDialect() && (!(getAttributes().containsKey( DIALECT )) || hasPackageDialect( "java" )) ) {
            return true;
        }

        return false;
    }

    private boolean isMvelDialect() {
        if ( context.isMvelDialect() ) {
            return true;
        } else if ( context.isDefaultDialect() && hasPackageDialect( "mvel" ) ) {
            return true;
        }
        return false;
    }

    private boolean hasPackageDialect(String dialect) {
        String globalDialect = (String) getAttributes().get( DIALECT );
        if ( globalDialect != null && dialect.equalsIgnoreCase( globalDialect ) ) {
            return true;
        }
        return false;
    }

    protected void addLHSCompletionProposals(List<ICompletionProposal> list,
                                             int documentOffset,
                                             Location location,
                                             String prefix,
                                             String backText) {
        switch ( location.getType() ) {
            case Location.LOCATION_LHS_BEGIN_OF_CONDITION :
                // if we are at the beginning of a new condition
                // add drools keywords
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "and",
                                                      "and ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "or",
                                                      "or ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "from",
                                                      "from ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "forall",
                                                      "forall(  )",
                                                      8,
                                                      DROOLS_ICON ) );
                RuleCompletionProposal prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                          prefix.length(),
                                                                          "eval",
                                                                          "eval(  )",
                                                                          6 );
                prop.setImage( DROOLS_ICON );
                list.add( prop );
                prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                   prefix.length(),
                                                   "then",
                                                   "then" + System.getProperty( "line.separator" ) + "\t" );
                prop.setImage( DROOLS_ICON );
                list.add( prop );
                // we do not break but also add all elements that are needed for
                // and/or
            case Location.LOCATION_LHS_BEGIN_OF_CONDITION_AND_OR :
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "not",
                                                      "not ",
                                                      DROOLS_ICON ) );
                // we do not break but also add all elements that are needed for
                // not
            case Location.LOCATION_LHS_BEGIN_OF_CONDITION_NOT :
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "exists",
                                                      "exists ",
                                                      DROOLS_ICON ) );
                // we do not break but also add all elements that are needed for
                // exists
            case Location.LOCATION_LHS_FROM_ACCUMULATE :
            case Location.LOCATION_LHS_FROM_COLLECT :
            case Location.LOCATION_LHS_BEGIN_OF_CONDITION_EXISTS :
                // and add imported classes
                Iterator<String> iterator = getImports().iterator();
                while ( iterator.hasNext() ) {
                    String name = iterator.next();
                    int index = name.lastIndexOf( "." );
                    if ( index != -1 ) {
                        String className = name.substring( index + 1 );
                        RuleCompletionProposal p = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                               prefix.length(),
                                                                               className,
                                                                               className + "(  )",
                                                                               className.length() + 2 );
                        p.setPriority( -1 );
                        p.setImage( CLASS_ICON );
                        list.add( p );
                    }
                }
                iterator = getClassesInPackage().iterator();
                while ( iterator.hasNext() ) {
                    String name = iterator.next();
                    int index = name.lastIndexOf( "." );
                    if ( index != -1 ) {
                        String className = name.substring( index + 1 );
                        RuleCompletionProposal p = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                               prefix.length(),
                                                                               className,
                                                                               className + "(  )",
                                                                               className.length() + 2 );
                        p.setPriority( -1 );
                        p.setImage( CLASS_ICON );
                        list.add( p );
                    }
                }
                break;
            case Location.LOCATION_LHS_INSIDE_CONDITION_START :
                String className = (String) location.getProperty( Location.LOCATION_PROPERTY_CLASS_NAME );
                String propertyName = (String) location.getProperty( Location.LOCATION_PROPERTY_PROPERTY_NAME );
                if ( className != null ) {
                        ClassTypeResolver resolver = new ClassTypeResolver( getUniqueImports(),
                                                                            ProjectClassLoader.getProjectClassLoader( getEditor() ) );
                        try {
                            String currentClass = className;
                            if ( propertyName != null ) {
                                String[] nestedProperties = propertyName.split( "\\." );
                                int nbSuperProperties = nestedProperties.length - 1;
                                if ( propertyName.endsWith( "." ) ) {
                                    nbSuperProperties++;
                                }
                                for ( int i = 0; i < nbSuperProperties; i++ ) {
                                    String simplePropertyName = nestedProperties[i];
                                    currentClass = getSimplePropertyClass( currentClass,
                                                                           simplePropertyName );
                                    currentClass = convertToNonPrimitiveClass( currentClass );
                                }
                            }
                            RuleCompletionProposal p = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                                   prefix.length(),
                                                                                   "this" );
                            p.setImage( METHOD_ICON );
                            list.add( p );
                            Class<?> clazz = resolver.resolveType( currentClass );
                            if ( clazz != null ) {
                                if ( Map.class.isAssignableFrom( clazz ) ) {
                                    p = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                    prefix.length(),
                                                                    "this['']",
                                                                    "this['']",
                                                                    6 );
                                    p.setImage( METHOD_ICON );
                                    list.add( p );
                                }
                                ClassFieldInspector inspector = new ClassFieldInspector( clazz );
                                Map<String, Class<?>> types = inspector.getFieldTypes();
                                Iterator<String> iterator2 = inspector.getFieldNames().keySet().iterator();
                                while ( iterator2.hasNext() ) {
                                    String name = iterator2.next();
                                    p = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                    prefix.length(),
                                                                    name,
                                                                    name + " " );
                                    p.setImage( METHOD_ICON );
                                    list.add( p );
                                    Class<?> type = types.get( name );
                                    if ( type != null && Map.class.isAssignableFrom( type ) ) {
                                        name += "['']";
                                        p = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                        prefix.length(),
                                                                        name,
                                                                        name,
                                                                        name.length() - 2 );
                                        p.setImage( METHOD_ICON );
                                        list.add( p );
                                    }
                                }
                            }
                        } catch ( IOException exc ) {
                            // Do nothing
                        } catch ( ClassNotFoundException exc ) {
                            // Do nothing
                        }
                }
                break;
            case Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR :
                className = (String) location.getProperty( Location.LOCATION_PROPERTY_CLASS_NAME );
                String property = (String) location.getProperty( Location.LOCATION_PROPERTY_PROPERTY_NAME );
                String type = getPropertyClass( className,
                                                property );

                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "==",
                                                      "== ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "!=",
                                                      "!= ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      ":",
                                                      ": ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "->",
                                                      "-> (  )",
                                                      5,
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "memberOf",
                                                      "memberOf ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "not memberOf",
                                                      "not memberOf ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "in",
                                                      "in (  )",
                                                      5,
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "not in",
                                                      "not in (  )",
                                                      9,
                                                      DROOLS_ICON ) );

                if ( isComparable( type ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "<",
                                                          "< ",
                                                          DROOLS_ICON ) );
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "<=",
                                                          "<= ",
                                                          DROOLS_ICON ) );
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          ">",
                                                          "> ",
                                                          DROOLS_ICON ) );
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          ">=",
                                                          ">= ",
                                                          DROOLS_ICON ) );
                }
                if ( type.equals( "java.lang.String" ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "matches",
                                                          "matches \"\"",
                                                          9,
                                                          DROOLS_ICON ) );
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "not matches",
                                                          "not matches \"\"",
                                                          13,
                                                          DROOLS_ICON ) );
                }
                if ( isSubtypeOf( type,
                                  "java.util.Collection" ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "contains",
                                                          "contains ",
                                                          DROOLS_ICON ) );
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "not contains",
                                                          "not contains ",
                                                          DROOLS_ICON ) );
                }
                break;
            case Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT :
                // determine type
                className = (String) location.getProperty( Location.LOCATION_PROPERTY_CLASS_NAME );
                property = (String) location.getProperty( Location.LOCATION_PROPERTY_PROPERTY_NAME );
                String operator = (String) location.getProperty( Location.LOCATION_PROPERTY_OPERATOR );
                type = getPropertyClass( className,
                                         property );

                if ( "in".equals( operator ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "()",
                                                          "(  )",
                                                          2,
                                                          DROOLS_ICON ) );
                    break;
                }

                if ( "contains".equals( operator ) || "excludes".equals( operator ) ) {
                    type = "java.lang.Object";
                }

                if ( "memberOf".equals( operator ) ) {
                    type = "java.util.Collection";
                }

                boolean isObject = false;
                if ( "java.lang.Object".equals( type ) ) {
                    isObject = true;
                }

                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "null",
                                                      "null ",
                                                      DROOLS_ICON ) );
                if ( "boolean".equals( type ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "true",
                                                          "true ",
                                                          DROOLS_ICON ) );
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "false",
                                                          "false ",
                                                          DROOLS_ICON ) );
                }
                if ( isObject || "java.lang.String".equals( type ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "\"\"",
                                                          "\"\"",
                                                          1,
                                                          DROOLS_ICON ) );
                }
                if ( isObject || "java.util.Date".equals( type ) ) {
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "\"dd-mmm-yyyy\"",
                                                          "\"dd-mmm-yyyy\"",
                                                          1,
                                                          DROOLS_ICON ) );
                }
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "()",
                                                      "(  )",
                                                      2,
                                                      DROOLS_ICON ) );
                // add parameters with possibly matching type
                Map<String, String> result = new HashMap<String, String>();
                addRuleParameters( result, context.getRuleParameters() );
                Iterator<Entry<String, String>> iterator2 = result.entrySet().iterator();
                while ( iterator2.hasNext() ) {
                    Entry<String, String> entry = iterator2.next();
                    String paramName = entry.getKey();
                    String paramType = entry.getValue();
                    if ( isSubtypeOf( paramType,
                                      type ) ) {
                        RuleCompletionProposal proposal = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                                      prefix.length(),
                                                                                      paramName );
                        proposal.setPriority( -1 );
                        proposal.setImage( VARIABLE_ICON );
                        list.add( proposal );
                    }
                }
                // add globals with possibly matching type
                List<GlobalDescr> globals = getGlobals();
                if ( globals != null ) {
                    for ( GlobalDescr global: globals ) {
                        if ( isSubtypeOf( global.getType(),
                                          type ) ) {
                            RuleCompletionProposal proposal = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                                          prefix.length(),
                                                                                          global.getIdentifier() );
                            proposal.setPriority( -1 );
                            proposal.setImage( VARIABLE_ICON );
                            list.add( proposal );
                        }
                    }
                }
                break;
            case Location.LOCATION_LHS_INSIDE_EVAL :
                String content = (String) location.getProperty( Location.LOCATION_EVAL_CONTENT );
                list.addAll( getJavaCompletionProposals( documentOffset,
                                                         content,
                                                         prefix,
                                                         getRuleParameters( backText ) ) );
                break;
            case Location.LOCATION_LHS_INSIDE_CONDITION_END :
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "&&",
                                                      "&& ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      "||",
                                                      "|| ",
                                                      DROOLS_ICON ) );
                list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                      prefix.length(),
                                                      ",",
                                                      ", ",
                                                      DROOLS_ICON ) );
                break;
            case Location.LOCATION_LHS_FROM :
                String fromText = (String) location.getProperty( Location.LOCATION_FROM_CONTENT );
                int index = fromText.indexOf( '.' );
                if ( index == -1 ) {
                    // add accumulate and collect keyword
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "accumulate",
                                                          "accumulate (  , init (  ), action (  ), result (  ) )",
                                                          13,
                                                          DROOLS_ICON ) );
                    KnowledgeBuilderConfigurationImpl config = new KnowledgeBuilderConfigurationImpl( ProjectClassLoader.getProjectClassLoader( getEditor() ),
                                                                                                      null );
                    for (String accumulateFunction : config.getAccumulateFunctionNames()) {
                        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                              prefix.length(),
                                                              "accumulate " + accumulateFunction,
                                                              "accumulate (  , " + accumulateFunction + "(  ) )",
                                                              13,
                                                              DROOLS_ICON ) );
                    }
                    list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                                          prefix.length(),
                                                          "collect",
                                                          "collect (  )",
                                                          10,
                                                          DROOLS_ICON ) );
                    // add all functions
                    if ( "".equals( fromText ) ) {
                        List<String> functions = getFunctions();
                        iterator = functions.iterator();
                        while ( iterator.hasNext() ) {
                            String name = iterator.next() + "()";
                            prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                               prefix.length(),
                                                               name,
                                                               name,
                                                               name.length() - 1 );
                            prop.setPriority( -1 );
                            prop.setImage( METHOD_ICON );
                            list.add( prop );
                        }
                    }
                    list.addAll( getJavaCompletionProposals( documentOffset,
                                                             fromText,
                                                             prefix,
                                                             getRuleParameters( backText ) ) );
                }
                break;
            case Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE :
                content = (String) location.getProperty( Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT );
                list.addAll( getJavaCompletionProposals( documentOffset,
                                                         content,
                                                         prefix,
                                                         getRuleParameters( backText ) ) );
                break;
            case Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION_INSIDE :
                content = (String) location.getProperty( Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT );
                content += (String) location.getProperty( Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT );
                list.addAll( getJavaCompletionProposals( documentOffset,
                                                         content,
                                                         prefix,
                                                         getRuleParameters( backText ) ) );
                break;
            case Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT_INSIDE :
                content = (String) location.getProperty( Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT );
                content += (String) location.getProperty( Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT );
                content += (String) location.getProperty( Location.LOCATION_PROPERTY_FROM_ACCUMULATE_RESULT_CONTENT );
                list.addAll( getJavaCompletionProposals( documentOffset,
                                                         content,
                                                         prefix,
                                                         getRuleParameters( backText ) ) );
                break;
        }
    }

    private String getPropertyClass(String className,
                                    String propertyName) {
        if ( className != null && propertyName != null ) {
                String[] nestedProperties = propertyName.split( "\\." );
                String currentClass = className;
                for ( int i = 0; i < nestedProperties.length; i++ ) {
                    String simplePropertyName = nestedProperties[i];
                    currentClass = getSimplePropertyClass( currentClass,
                                                           simplePropertyName );
                }
                return currentClass;
        }
        return null;
    }

    private String getSimplePropertyClass(String className,
                                          String propertyName) {
        if ( "this".equals( propertyName ) ) {
            return className;
        }
        if ( propertyName.endsWith( "]" ) ) {
            // TODO can we take advantage of generics here?
            return "java.lang.Object";
        }
        ClassTypeResolver resolver = new ClassTypeResolver( getUniqueImports(),
                                                            ProjectClassLoader.getProjectClassLoader( getEditor() ) );
        try {
            Class<?> clazz = resolver.resolveType( className );
            if ( clazz != null ) {
                Class<?> clazzz = new ClassFieldInspector( clazz ).getFieldTypes().get( propertyName );
                if ( clazzz != null ) {
                    return clazzz.getName();
                }
            }
        } catch ( IOException exc ) {
            // Do nothing
        } catch ( ClassNotFoundException exc ) {
            // Do nothing
        }
        return "java.lang.Object";
    }

    private Map<String, String> getRuleParameters(String backText) {
        Map<String, String> result = new HashMap<String, String>();
        // add globals
        List<GlobalDescr> globals = getGlobals();
        if ( globals != null ) {
            for (GlobalDescr global : globals) {
                result.put( global.getIdentifier(),
                            global.getType() );
            }
        }

        if ( context == null ) {
            context = new CompletionContext( backText );
        }
        addRuleParameters( result, context.getRuleParameters() );
        return result;
    }

    private boolean isComparable(String type) {
        if ( type == null ) {
            return false;
        }
        if ( isPrimitiveNumericType( type ) ) {
            return true;
        }
        if ( isObjectNumericType( type ) ) {
            return true;
        }
        if ( isSubtypeOf( type,
                          "java.lang.Comparable" ) ) {
            return true;
        }
        return false;
    }

    private boolean isPrimitiveType(String type) {
        return isPrimitiveNumericType( type ) || type.equals( "boolean" );
    }

    private boolean isPrimitiveNumericType(String type) {
        return type.equals( "byte" ) || type.equals( "short" ) || type.equals( "int" ) || type.equals( "long" ) || type.equals( "float" ) || type.equals( "double" ) || type.equals( "char" );
    }

    private boolean isObjectNumericType(String type) {
        return type.equals( "java.lang.Byte" ) || type.equals( "java.lang.Short" ) || type.equals( "java.lang.Integer" ) || type.equals( "java.lang.Long" ) || type.equals( "java.lang.Float" ) || type.equals( "java.lang.Double" )
               || type.equals( "java.lang.Char" );
    }

    /**
     * Returns true if the first class is the same or a subtype of the second
     * class.
     *
     * @param class1
     * @param class2
     * @return
     */
    private boolean isSubtypeOf(String class1,
                                String class2) {
        if ( class1 == null || class2 == null ) {
            return false;
        }
        class1 = convertToNonPrimitiveClass( class1 );
        class2 = convertToNonPrimitiveClass( class2 );
        // TODO add code to take primitive types into account
        ClassTypeResolver resolver = new ClassTypeResolver( getUniqueImports(),
                                                            ProjectClassLoader.getProjectClassLoader( getEditor() ) );
        try {
            Class<?> clazz1 = resolver.resolveType( class1 );
            Class<?> clazz2 = resolver.resolveType( class2 );
            if ( clazz1 == null || clazz2 == null ) {
                return false;
            }
            return clazz2.isAssignableFrom( clazz1 );
        } catch ( ClassNotFoundException exc ) {
            return false;
        }
    }

    private String convertToNonPrimitiveClass(String clazz) {
        if ( !isPrimitiveType( clazz ) ) {
            return clazz;
        }
        if ( "byte".equals( clazz ) ) {
            return "java.lang.Byte";
        } else if ( "short".equals( clazz ) ) {
            return "java.lang.Short";
        } else if ( "int".equals( clazz ) ) {
            return "java.lang.Integer";
        } else if ( "long".equals( clazz ) ) {
            return "java.lang.Long";
        } else if ( "float".equals( clazz ) ) {
            return "java.lang.Float";
        } else if ( "double".equals( clazz ) ) {
            return "java.lang.Double";
        } else if ( "char".equals( clazz ) ) {
            return "java.lang.Char";
        } else if ( "boolean".equals( clazz ) ) {
            return "java.lang.Boolean";
        }
        // should never occur
        return null;
    }

    private void addRHSFunctionCompletionProposals(List<ICompletionProposal> list, int documentOffset, String prefix) {
        Iterator<String> iterator;
        RuleCompletionProposal prop;
        List<String> functions = getFunctions();
        iterator = functions.iterator();
        while ( iterator.hasNext() ) {
            String name = iterator.next() + "()";
            prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                               prefix.length(),
                                               name,
                                               name + ";",
                                               name.length() - 1 );
            prop.setPriority( -1 );
            prop.setImage( METHOD_ICON );
            list.add( prop );
        }
    }

    private void addRHSKeywordCompletionProposals(List<ICompletionProposal> list, int documentOffset, String prefix) {
        RuleCompletionProposal prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                  prefix.length(),
                                                                  "update",
                                                                  "update();",
                                                                  7 );
        prop.setImage( DROOLS_ICON );
        list.add( prop );
        prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                           prefix.length(),
                                           "retract",
                                           "retract();",
                                           8 );
        prop.setImage( DROOLS_ICON );
        list.add( prop );
        prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                           prefix.length(),
                                           "insert",
                                           "insert();",
                                           7 );
        prop.setImage( DROOLS_ICON );
        list.add( prop );
        prop = new RuleCompletionProposal( documentOffset - prefix.length(),
                                           prefix.length(),
                                           "insertLogical",
                                           "insertLogical();",
                                           14 );
        prop.setImage( DROOLS_ICON );
        list.add( prop );
    }

    private void addRHSJavaCompletionProposals(List<ICompletionProposal> list,
                                               int documentOffset,
                                               String prefix,
                                               String backText,
                                               String consequence) {
        list.addAll( getJavaCompletionProposals( documentOffset,
                                                 consequence,
                                                 prefix,
                                                 getRuleParameters( backText ) ) );
    }

    private void addRHSMvelCompletionProposals(List<ICompletionProposal> list,
                                               final int documentOffset,
                                               String prefix,
                                               String backText,
                                               String consequence,
                                               boolean expressionStart) {

        Collection<ICompletionProposal> mvelCompletionProposals = getMvelCompletionProposals( consequence,
                                                                         documentOffset,
                                                                         prefix,
                                                                         getRuleParameters( backText ),
                                                                         backText,
                                                                         expressionStart );
        list.addAll( mvelCompletionProposals );
    }

    private Collection<ICompletionProposal> getMvelCompletionProposals(final String consequenceBackText,
                                                  final int documentOffset,
                                                  final String prefix,
                                                  Map<String, String> params,
                                                  String ruleBackText,
                                                  boolean startOfExpression) {

        final Set<ICompletionProposal> proposals = new HashSet<ICompletionProposal>();

        if (!(getEditor().getEditorInput() instanceof IFileEditorInput)) {
            return proposals;
        }

        try {
            DRLInfo drlInfo = DroolsEclipsePlugin.getDefault().generateParsedResource(
                "package dummy; \n" + ruleBackText,
                ((IFileEditorInput) getEditor().getEditorInput()).getFile(),
                false,
                false );

            String textWithoutPrefix = CompletionUtil.getTextWithoutPrefix( consequenceBackText,
                                                                            prefix );
            boolean expressionStart = CompletionUtil.isStartOfDialectExpression( textWithoutPrefix );

            boolean isConstrained = textWithoutPrefix.endsWith( "." );

            // we split the expression in various regions:
            // *the previous expression
            // *the last expression
            // *the last inner expression

            // attempt to compile and analyze the previous expression to collect inputs and vars
            String previousExpression = CompletionUtil.getPreviousExpression( consequenceBackText );
            MvelContext previousExprContext = analyzeMvelExpression( getResolvedMvelInputs( params ),
                                                                     drlInfo,
                                                                     previousExpression );

            // attempt to compile and analyze the last and last inner expression, using as inputs the previous expression inputs and vars
            @SuppressWarnings("rawtypes")
            Map<String, Class> variables = previousExprContext.getContext().getVariables();
            @SuppressWarnings("rawtypes")
            Map<String, Class> inputs = previousExprContext.getContext().getInputs();
            inputs.putAll( variables );

            //last inner expression
            String lastInnerExpression = CompletionUtil.getTextWithoutPrefix( CompletionUtil.getInnerExpression( consequenceBackText ),
                                                                              prefix );
            String compilableLastInnerExpression = CompletionUtil.getCompilableText( lastInnerExpression );

            MvelContext lastInnerExprContext = analyzeMvelExpression( inputs,
                                                                      drlInfo,
                                                                      compilableLastInnerExpression );

            //last expression
            String lastExpression = CompletionUtil.getLastExpression( consequenceBackText ).trim();
            //is this a modify expression?
            //group 1 is the body of modify
            //group 2 if present is the whole with block including brackets
            //group 3 if present is the inner content of the with block
            Matcher modMatcher = CompletionUtil.MODIFY_PATTERN.matcher( lastExpression );

            boolean isModifyBlock = modMatcher.matches() && modMatcher.groupCount() == 3;

            //if constrained, get completion for egress of last inner, filtered on prefix
            if ( isConstrained ) {
                if ( lastInnerExprContext.isStaticFlag() ) {
                    return getMvelClassCompletionsFromJDT( documentOffset,
                                                           "",
                                                           params,
                                                           lastInnerExprContext.getReturnedType() );

                }

                return getMvelInstanceCompletionsFromJDT( documentOffset,
                                                          "",
                                                          params,
                                                          lastInnerExprContext.getReturnedType(),
                                                          false );
            }
            //if expression start inside with block, then get completion for prefix with egrss of modif var + prev expr var&inputs
            else if ( expressionStart && isModifyBlock ) {
                String modifyVar = modMatcher.group( 1 );
                //String modifyWith = modMatcher.group( 3 );

                //get the egress type of the modify var
                MvelContext modVarContext = analyzeMvelExpression( inputs,
                                                                   drlInfo,
                                                                   modifyVar );

                Class<?> modVarType = modVarContext.getReturnedType();

                Collection<ICompletionProposal> modVarComps = getMvelInstanceCompletionsFromJDT( documentOffset,
                                                                            "",
                                                                            params,
                                                                            modVarType,
                                                                            true );

                proposals.addAll( modVarComps );

                //                addMvelCompletions( proposals,
                //                                    documentOffset,
                //                                    "",
                //                                    lastInnerExprContext.getContext().getVariables() );
                //
                //                addMvelCompletions( proposals,
                //                                    documentOffset,
                //                                    "",
                //                                    lastInnerExprContext.getContext().getInputs() );
                //
                //                Collection jdtProps = getJavaCompletionProposals( documentOffset,
                //                                                                  prefix,
                //                                                                  prefix,
                //                                                                  params );
                //
                //                proposals.addAll( jdtProps );
                return proposals;

            }
            //If expression start, and all other cases then get completion for prefix with prev expr var&inputs
            addMvelCompletions( proposals,
                                documentOffset,
                                prefix,
                                lastInnerExprContext.getContext().getVariables() );

            addMvelCompletions( proposals,
                                documentOffset,
                                prefix,
                                lastInnerExprContext.getContext().getInputs() );

            Collection<ICompletionProposal> jdtProps = getJavaCompletionProposals( documentOffset,
                                                              prefix,
                                                              prefix,
                                                              params );

            proposals.addAll( jdtProps );

        } catch ( Throwable e ) {
            DroolsEclipsePlugin.log( e );
        }
        Set<ICompletionProposal> uniqueProposals = new HashSet<ICompletionProposal>();
        addAllNewProposals( uniqueProposals,
                            proposals );
        return uniqueProposals;
    }
    
    @SuppressWarnings("rawtypes")
    private Map<String, Class> getResolvedMvelInputs(Map<String, String> params) {
        ClassTypeResolver resolver = new ClassTypeResolver(getUniqueImports(),
                ProjectClassLoader.getProjectClassLoader(getEditor()));

        Map<String, Class> resolved = new HashMap<String, Class>();
        for (Entry<String, String> entry : params.entrySet()) {
            String inputType = entry.getValue();
            try {
                Class<?> type = resolver.resolveType( inputType );
                resolved.put( entry.getKey(), type );
            } catch ( ClassNotFoundException e ) {
                DroolsEclipsePlugin.log( e );
            }
        }
        return resolved;
    }

    class MvelContext {
        private CompiledExpression expression;
        private ParserContext      parserContext;
        private Class<?>              returnedType;
        private boolean            staticFlag;

        public ParserContext getContext() {
            return parserContext;
        }

        void setContext(ParserContext parserContext) {
            this.parserContext = parserContext;
        }

        void setExpression(CompiledExpression expression) {
            this.expression = expression;
        }

        CompiledExpression getExpression() {
            return expression;
        }

        void setReturnedType(Class<?> returnedType) {
            this.returnedType = returnedType;
        }

        Class<?> getReturnedType() {
            return returnedType;
        }

        public boolean isStaticFlag() {
            return staticFlag;
        }

        public void setStaticFlag(boolean staticFlag) {
            this.staticFlag = staticFlag;
        }
    }

    private MvelContext analyzeMvelExpression(@SuppressWarnings("rawtypes") Map<String, Class> params,
                                              DRLInfo drlInfo,
                                              String mvel) {

        String expr = processMacros( mvel );

        String name = context.getRuleName();
        RuleInfo currentRule = getCurrentRule( drlInfo,
                                               name );
        String qName = drlInfo.getPackageName() + "." + name;
        MVELDialect dialect = (MVELDialect) drlInfo.getDialectRegistry().getDialect("mvel");
        ParserContext initialContext = createInitialContext( params,
                                                             qName,
                                                             dialect );
        MvelContext mCon = new MvelContext();
        mCon.setContext( initialContext );

        try {
        	ExecutableStatement stmt = (ExecutableStatement) MVEL.compileExpression(expr, initialContext);
            Class<?> lastType = stmt.getKnownEgressType();

            //Statics expression may return Class as an egress type
            if ( lastType != null && "java.lang.Class".equals( lastType.getName() ) ) {
                mCon.setStaticFlag( true );
            }

            if ( lastType == null || "java.lang.Object".equals( lastType.getName() ) || "java.lang.Class".equals( lastType.getName() ) ) {
                // attempt to use the property verifier to get
                // a better type  resolution (a recommend by cbrock, though egress gives consistent results)
                lastType = new PropertyVerifier( expr, initialContext ).analyze();
            }

            if ( lastType == null ) {
                lastType = Object.class;
            }

            mCon.setReturnedType( lastType );
        } catch ( Exception e ) {
            //do nothing while doing completion.
        }
        return mCon;
    }

    @SuppressWarnings("unchecked")
    private static ParserContext createInitialContext(@SuppressWarnings("rawtypes") Map<String, Class> params,
                                                      String qualifiedName,
                                                      MVELDialect dialect) {

        //Lookup ParserConfiguration to retrieve imports and package imports
        PackageRegistry packageRegistry = dialect.getPackageRegistry();
        MVELDialectRuntimeData data = (MVELDialectRuntimeData) packageRegistry.getDialectRuntimeRegistry().getDialectData( dialect.getId() );
        ParserConfiguration pconf = data.getParserConfiguration();

        final ParserContext context = new ParserContext( pconf.getImports(),
                                                         null,
                                                         qualifiedName );

        if ( pconf.getPackageImports() != null ) {
            for (String packageImport : pconf.getPackageImports()) {
                context.addPackageImport( packageImport );
            }
        }
        context.setStrictTypeEnforcement( false );

        context.setInterceptors( dialect.getInterceptors() );
        context.setInputs( params );
        context.addInput("drools", KnowledgeHelper.class);
        context.addInput("kcontext", RuleContext.class);
        context.setCompiled( true );
        return context;
    }

    public static String processMacros(String mvel) {
        String macrosProcessedCompilableConsequence = MVELConsequenceBuilder.processMacros( mvel.trim() );
        return macrosProcessedCompilableConsequence;
    }

    private static RuleInfo getCurrentRule(DRLInfo drlInfo,
                                    String currentRulename) {
        RuleInfo currentRule = null;
        RuleInfo[] ruleInfos = drlInfo.getRuleInfos();
        for ( int i = 0; i < ruleInfos.length; i++ ) {
            if ( currentRulename.equals( ruleInfos[i].getRuleName() ) ) {
                currentRule = ruleInfos[i];
                break;
            }
        }
        return currentRule;
    }

    /*
     * Completions for object instance members
     */
    private Collection<ICompletionProposal> getMvelInstanceCompletionsFromJDT(final int documentOffset,
                                                         final String prefix,
                                                         Map<String, String> params,
                                                         Class<?> lastType,
                                                         boolean settersOnly) {
        if ( lastType == null ) {
            lastType = Object.class;
        }

        //FIXME: there is a small chance of var name collision using this arbitrary mvdrlofc as a variable name.
        //ideally the variable name should be inferred from the last member of the expression
        final String syntheticVarName = "mvdrlofc";

        String javaText = "\n" + lastType.getPackage().getName() + "." + CompletionUtil.getSimpleClassName( lastType ) + " " + syntheticVarName + ";\n" + syntheticVarName + ".";
        final List<ICompletionProposal> list1 = new ArrayList<ICompletionProposal>();
        requestJavaCompletionProposals(javaText, prefix, documentOffset, params, list1);

        final List<ICompletionProposal> list = list1;

        Collection<ICompletionProposal> mvelList = RuleCompletionProcessor.mvelifyProposals(list, settersOnly);
        return mvelList;
    }

    /*
     * Completions for static Class members
     */
    private Collection<ICompletionProposal> getMvelClassCompletionsFromJDT(final int documentOffset,
                                                      final String prefix,
                                                      Map<String, String> params,
                                                      Class<?> lastType) {
        if ( lastType == null ) {
            lastType = Object.class;
        }

        //FIXME: there is a small chance of var name collision using this arbitrary mvdrlofc as a variable name.
        //ideally the variable name should be inferred from the last member of the expression

        String javaText = "\n" + CompletionUtil.getSimpleClassName( lastType ) + ".";
        final List<ICompletionProposal> list1 = new ArrayList<ICompletionProposal>();
        requestJavaCompletionProposals(javaText, prefix, documentOffset, params, list1);
        final List<ICompletionProposal> list = list1;
        Collection<ICompletionProposal> mvelList = RuleCompletionProcessor.mvelifyProposals(list, false);
        return mvelList;
    }

    @SuppressWarnings("rawtypes")
    private static void addMvelCompletions(final Collection<ICompletionProposal> proposals,
                                    int documentOffset,
                                    String prefix,
                                    Map<String, Class> inputs) {
        Set<ICompletionProposal> newProposals = new HashSet<ICompletionProposal>();
        for (Entry<String, Class> entry : inputs.entrySet()) {
            String prop = entry.getKey();
            Class<?> type = entry.getValue();
            String display = prop + "  " + CompletionUtil.getSimpleClassName( type );

            RuleCompletionProposal rcp = new RuleCompletionProposal( documentOffset - prefix.length(),
                                                                     prefix.length(),
                                                                     display,
                                                                     prop );
            rcp.setImage( DefaultCompletionProcessor.VARIABLE_ICON );
            newProposals.add( rcp );
        }
        addAllNewProposals(proposals, newProposals);
    }

    public static void addAllNewProposals(final Collection<ICompletionProposal> proposals,
                                          final Collection<ICompletionProposal> newProposals) {
        for (ICompletionProposal newProp : newProposals) {
            String displayString = newProp.getDisplayString();

            //JBRULES-1134 do not add completions if they already exist
            if (!containsProposal(proposals, displayString)) {
                proposals.add(newProp);
            }
        }
    }

    /**
     * Attempt to compare proposals of different types based on the tokenized display string
     * @param proposals
     * @param newProposal
     * @return true if the collection contains a proposal which matches the new Proposal.
     * The match is based on the first token based on a space split
     */
    public static boolean containsProposal(final Collection<ICompletionProposal> proposals, String newProposal) {
        for (ICompletionProposal prop : proposals) {
            String displayString = prop.getDisplayString();
            String[] existings = displayString.split( " " );
            if ( existings.length == 0 ) {
                continue;
            }

            String[] newProposals = newProposal.split( " " );
            if ( newProposals.length == 0 ) {
                continue;
            }

            if ( existings[0].equals( newProposals[0] ) ) {
                return true;
            }
        }
        return false;
    }

    private void addRuleParameters(Map<String, String> result,
                                   Map<String, String[]> ruleParameters) {
        for (Map.Entry<String, String[]> entry: ruleParameters.entrySet()) {
            String name = entry.getKey();
            String clazz = entry.getValue()[0];
            String field = entry.getValue()[1];
            String type;
            if (field == null) {
                type = clazz;
            } else {
                type = getPropertyClass( clazz, field );
            }
            result.put( name, type );
        }
    }

    private void addRuleHeaderProposals(List<ICompletionProposal> list,
                                        int documentOffset,
                                        String prefix,
                                        String backText) {
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "salience",
                                              "salience ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "no-loop",
                                              "no-loop ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "agenda-group",
                                              "agenda-group ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "duration",
                                              "duration ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "auto-focus",
                                              "auto-focus ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "when",
                                              "when" + System.getProperty( "line.separator" ) + "\t ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "activation-group",
                                              "activation-group ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "date-effective",
                                              "date-effective \"dd-MMM-yyyy\"",
                                              16,
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "date-expires",
                                              "date-expires \"dd-MMM-yyyy\"",
                                              14,
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "enabled",
                                              "enabled false",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "ruleflow-group",
                                              "ruleflow-group \"\"",
                                              16,
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "lock-on-active",
                                              "lock-on-active ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "dialect \"java\"",
                                              "dialect \"java\" ",
                                              DROOLS_ICON ) );
        list.add( new RuleCompletionProposal( documentOffset - prefix.length(),
                                              prefix.length(),
                                              "dialect \"mvel\"",
                                              "dialect \"mvel\" ",
                                              DROOLS_ICON ) );
    }

    /*
     * Filters accessor method proposals to replace them with their mvel expression equivalent
     * For instance a completion for getStatus() would be replaced by a completion for status
     * when asking for stters only, then only setters or writable fields will be returned
     */
    public static Collection<ICompletionProposal> mvelifyProposals(List<ICompletionProposal> list, boolean settersOnly) {
        final Collection<ICompletionProposal> set = new HashSet<ICompletionProposal>();

        for (ICompletionProposal o : list) {
            if ( o instanceof JavaMethodCompletionProposal ) {
                //methods
                processJavaMethodCompletionProposal(list, settersOnly, set, o);

            } else if ( o instanceof JavaCompletionProposal ) {
                //fields
                processesJavaCompletionProposal(settersOnly, set, o);
            } else if ( !settersOnly ) {
                set.add( o );
            }
        }
        return set;
    }

    private static void processesJavaCompletionProposal(boolean settersOnly,
                                                        final Collection<ICompletionProposal> set,
                                                        ICompletionProposal o) {
        if ( settersOnly ) {
            JavaCompletionProposal jcp = (JavaCompletionProposal) o;
            //TODO: FIXME: this is very fragile as it uses reflection to access the private completion field.
            //Yet this is needed to do mvel filtering based on the method signtures, IF we use the richer JDT completion
            //                    Object field = ReflectionUtils.getField( o,
            //                                                             "fProposal" );
            IJavaElement javaElement = jcp.getJavaElement();
            if ( javaElement.getElementType() == IJavaElement.FIELD ) {
                set.add( o );

            }
        } else {
            set.add( o );
        }
    }

    private static void processJavaMethodCompletionProposal(List<ICompletionProposal> list,
                                                            boolean settersOnly,
                                                            final Collection<ICompletionProposal> set,
                                                            ICompletionProposal o) {
        LazyJavaCompletionProposal javaProposal = (LazyJavaCompletionProposal) o;
        //TODO: FIXME: this is very fragile as it uses reflection to access the private completion field.
        //Yet this is needed to do mvel filtering based on the method signtures, IF we use the richer JDT completion
        Object field = ReflectionUtils.getField(o, "fProposal");
        if ( field != null && field instanceof CompletionProposal ) {
            CompletionProposal proposal = (CompletionProposal) field;

            String completion = new String( proposal.getCompletion() );

            String propertyOrMethodName = null;

            boolean isSetter = false;
            boolean isAccessor = false;
            if ( settersOnly ) {
                // get the eventual writable property name for that method name and signature
                propertyOrMethodName = CompletionUtil.getWritablePropertyName( completion,
                                                                               proposal.getSignature() );
                //                      if we got a property name that differs from the orginal method name
                //then this is a bean accessor
                isSetter = !completion.equals( propertyOrMethodName );

            } else {
                // get the eventual property name for that method name and signature
                propertyOrMethodName = CompletionUtil.getPropertyName( completion,
                                                                       proposal.getSignature() );
                //if we got a property name that differs from the orginal method name
                //then this is a bean accessor
                isAccessor = !completion.equals( propertyOrMethodName );
            }

            // is the completion for a bean accessor? and do we have already some relevant completion?
            boolean doesNotContainFieldCompletion = DefaultCompletionProcessor.doesNotContainFieldCompletion( propertyOrMethodName,
                                                                                                              list );
            if ( ((settersOnly && isSetter) || (!settersOnly && isAccessor)) && doesNotContainFieldCompletion ) {

                //TODO: craft a better JDTish display name than just the property name
                RuleCompletionProposal prop = new RuleCompletionProposal( javaProposal.getReplacementOffset(),
                                                                          javaProposal.getReplacementLength(),
                                                                          propertyOrMethodName );
                prop.setImage( DefaultCompletionProcessor.VARIABLE_ICON );
                //set high priority such that the completion for accessors shows up first
                prop.setPriority( 1000 );
                set.add( prop );

            }

            else if ( !settersOnly ) {
                set.add( o );
            }
        }
    }
}
