/*
 * 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.rule.MVELDialectRuntimeData;
import org.drools.core.spi.KnowledgeHelper;
import org.drools.core.util.asm.ClassFieldInspector;
import org.drools.eclipse.DRLInfo.RuleInfo;
import org.drools.eclipse.DRLInfo;
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.kie.soup.project.datamodel.commons.types.ClassTypeResolver;
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);
            }
        }
    }
}
