org.drools.eclipse.editors.completion.RuleCompletionProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.drools.eclipse.editors.completion.RuleCompletionProcessor.java

Source

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

package org.drools.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.Set;
import java.util.regex.Matcher;

import org.drools.base.ClassTypeResolver;
import org.drools.compiler.PackageBuilderConfiguration;
import org.drools.core.util.asm.ClassFieldInspector;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.DroolsPluginImages;
import org.drools.eclipse.DRLInfo.RuleInfo;
import org.drools.eclipse.editors.AbstractRuleEditor;
import org.drools.eclipse.util.ProjectClassLoader;
import org.drools.lang.Location;
import org.drools.lang.descr.FactTemplateDescr;
import org.drools.lang.descr.FieldTemplateDescr;
import org.drools.lang.descr.GlobalDescr;
import org.drools.rule.builder.dialect.mvel.MVELConsequenceBuilder;
import org.drools.rule.builder.dialect.mvel.MVELDialect;
import org.drools.runtime.rule.RuleContext;
import org.drools.spi.KnowledgeHelper;
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.mvel2.ParserContext;
import org.mvel2.compiler.CompiledExpression;
import org.mvel2.compiler.ExpressionCompiler;
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 getCompletionProposals(ITextViewer viewer, int documentOffset) {
        try {
            final List list = new ArrayList();
            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 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<RuleCompletionProposal> 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);
                }
            }
            iterator = getTemplates().iterator();
            while (iterator.hasNext()) {
                String name = (String) iterator.next();
                RuleCompletionProposal p = new RuleCompletionProposal(documentOffset - prefix.length(),
                        prefix.length(), name, name + "(  )", name.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) {
                boolean isTemplate = addFactTemplatePropertyProposals(documentOffset, prefix, className, list);
                if (!isTemplate) {
                    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 types = inspector.getFieldTypes();
                            Iterator iterator2 = inspector.getFieldNames().keySet().iterator();
                            while (iterator2.hasNext()) {
                                String name = (String) iterator2.next();
                                p = new RuleCompletionProposal(documentOffset - prefix.length(), prefix.length(),
                                        name, name + " ");
                                p.setImage(METHOD_ICON);
                                list.add(p);
                                Class type = (Class) 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 result = new HashMap();
            addRuleParameters(result, context.getRuleParameters());
            Iterator iterator2 = result.entrySet().iterator();
            while (iterator2.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator2.next();
                String paramName = (String) entry.getKey();
                String paramType = (String) 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));
                PackageBuilderConfiguration config = new PackageBuilderConfiguration(
                        ProjectClassLoader.getProjectClassLoader(getEditor()), null);
                Map accumulateFunctions = config.getAccumulateFunctionsMap();
                for (iterator2 = accumulateFunctions.keySet().iterator(); iterator2.hasNext();) {
                    String accumulateFunction = (String) iterator2.next();
                    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 functions = getFunctions();
                    iterator = functions.iterator();
                    while (iterator.hasNext()) {
                        String name = (String) 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) {
            FactTemplateDescr template = getTemplate(className);
            if (template != null) {
                Iterator iterator = template.getFields().iterator();
                while (iterator.hasNext()) {
                    FieldTemplateDescr field = (FieldTemplateDescr) iterator.next();
                    if (propertyName.equals(field.getName())) {
                        String type = field.getClassType();
                        if (isPrimitiveType(type)) {
                            return type;
                        }
                        ClassTypeResolver resolver = new ClassTypeResolver(getUniqueImports(),
                                ProjectClassLoader.getProjectClassLoader(getEditor()));
                        try {
                            Class clazz = resolver.resolveType(type);
                            if (clazz != null) {
                                return clazz.getName();
                            }
                        } catch (ClassNotFoundException exc) {
                            DroolsEclipsePlugin.log(exc);
                        }
                    }
                }
                // if not found, return null
            } else {
                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 = (Class) 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 getRuleParameters(String backText) {
        Map result = new HashMap();
        // add globals
        List globals = getGlobals();
        if (globals != null) {
            for (Iterator iterator = globals.iterator(); iterator.hasNext();) {
                GlobalDescr global = (GlobalDescr) iterator.next();
                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 list, int documentOffset, String prefix) {
        Iterator iterator;
        RuleCompletionProposal prop;
        List functions = getFunctions();
        iterator = functions.iterator();
        while (iterator.hasNext()) {
            String name = (String) 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 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 list, int documentOffset, String prefix, String backText,
            String consequence) {
        list.addAll(getJavaCompletionProposals(documentOffset, consequence, prefix, getRuleParameters(backText)));
    }

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

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

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

        final Set proposals = new HashSet();

        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
            Map variables = previousExprContext.getContext().getVariables();
            Map 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 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 jdtProps = getJavaCompletionProposals(documentOffset, prefix, prefix, params);

            proposals.addAll(jdtProps);

        } catch (Throwable e) {
            DroolsEclipsePlugin.log(e);
        }
        Set uniqueProposals = new HashSet();
        addAllNewProposals(uniqueProposals, proposals);
        return uniqueProposals;
    }

    private Map getResolvedMvelInputs(Map params) {
        ClassTypeResolver resolver = new ClassTypeResolver(getUniqueImports(),
                ProjectClassLoader.getProjectClassLoader(getEditor()));

        Map resolved = new HashMap();
        for (Iterator iter = params.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry) iter.next();
            String inputType = (String) 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 initialContext;
        private Class returnedType;
        private boolean staticFlag;

        public ParserContext getContext() {
            if (getExpression() != null) {
                if (getExpression().getParserContext() != null) {
                    return getExpression().getParserContext();
                }
            }
            return getInitialContext();
        }

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

        CompiledExpression getExpression() {
            return expression;
        }

        void setInitialContext(ParserContext initialContext) {
            this.initialContext = initialContext;
        }

        ParserContext getInitialContext() {
            return initialContext;
        }

        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(Map params, DRLInfo drlInfo, String mvel) {

        String macroMvel = 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.setInitialContext(initialContext);

        try {
            ExpressionCompiler compiler = new ExpressionCompiler(macroMvel);
            CompiledExpression expression = compiler.compile(initialContext);
            mCon.setExpression(expression);

            ParserContext compilationContext = compiler.getParserContextState();

            Class lastType = expression.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(macroMvel, compilationContext).analyze();
            }

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

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

    private static ParserContext createInitialContext(Map params, String qualifiedName, MVELDialect dialect) {

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

        if (dialect.getPackgeImports() != null) {
            for (Iterator it = dialect.getPackgeImports().iterator(); it.hasNext();) {
                String packageImport = (String) it.next();
                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) {
        MVELConsequenceBuilder builder = new MVELConsequenceBuilder();
        String macrosProcessedCompilableConsequence = builder.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 getMvelInstanceCompletionsFromJDT(final int documentOffset, final String prefix, Map 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 list1 = new ArrayList();
        requestJavaCompletionProposals(javaText, prefix, documentOffset, params, list1);

        final List list = list1;

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

    /*
     * Completions for static Class members
     */
    private Collection getMvelClassCompletionsFromJDT(final int documentOffset, final String prefix, Map 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 list1 = new ArrayList();
        requestJavaCompletionProposals(javaText, prefix, documentOffset, params, list1);
        final List list = list1;
        Collection mvelList = RuleCompletionProcessor.mvelifyProposals(list, false);
        return mvelList;
    }

    private static void addMvelCompletions(final Collection proposals, int documentOffset, String prefix,
            Map inputs) {
        Set newProposals = new HashSet();
        for (Iterator iter = inputs.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry) iter.next();
            String prop = (String) entry.getKey();

            Class type = (Class) 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 proposals, final Collection newProposals) {
        for (Iterator iter = newProposals.iterator(); iter.hasNext();) {
            ICompletionProposal newProp = (ICompletionProposal) iter.next();
            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 proposals, String newProposal) {
        for (Iterator iter = proposals.iterator(); iter.hasNext();) {
            ICompletionProposal prop = (ICompletionProposal) iter.next();
            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 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));
    }

    private boolean addFactTemplatePropertyProposals(int documentOffset, String prefix, String templateName,
            List list) {
        FactTemplateDescr descr = getTemplate(templateName);
        if (descr == null) {
            return false;
        }
        Iterator iterator = descr.getFields().iterator();
        while (iterator.hasNext()) {
            FieldTemplateDescr field = (FieldTemplateDescr) iterator.next();
            String fieldName = field.getName();
            RuleCompletionProposal p = new RuleCompletionProposal(documentOffset - prefix.length(), prefix.length(),
                    fieldName, fieldName + " ");
            p.setImage(METHOD_ICON);
            list.add(p);
        }
        return true;
    }

    /*
     * 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 mvelifyProposals(List list, boolean settersOnly) {
        final Collection set = new HashSet();

        for (Iterator iter = list.iterator(); iter.hasNext();) {
            Object o = iter.next();
            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 set, Object 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 list, boolean settersOnly, final Collection set,
            Object 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);
            }
        }
    }
}