All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.ide.common.client.modeldriven.SuggestionCompletionEngine Maven / Gradle / Ivy

There is a newer version: 5.1.1
Show newest version
/**
 * 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.ide.common.client.modeldriven;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.drools.ide.common.client.modeldriven.ModelField.FIELD_CLASS_TYPE;
import org.drools.ide.common.client.modeldriven.brl.ActionFieldValue;
import org.drools.ide.common.client.modeldriven.brl.DSLSentence;
import org.drools.ide.common.client.modeldriven.brl.FactPattern;
import org.drools.ide.common.client.modeldriven.brl.FieldConstraint;
import org.drools.ide.common.client.modeldriven.brl.PortableObject;
import org.drools.ide.common.client.modeldriven.brl.SingleFieldConstraint;

/**
 * An suggestion completion processor. This should be usable in both GWT/Web and
 * the IDE. The data for this can be loaded into this from simple string lists.
 * 
 * @author Michael Neale
 */
public class SuggestionCompletionEngine implements PortableObject {

    /** These are the explicit types supported */
    public static final String            TYPE_COLLECTION        = "Collection";
    public static final String            TYPE_COMPARABLE        = "Comparable";
    public static final String            TYPE_STRING            = "String";
    public static final String            TYPE_NUMERIC           = "Numeric";
    public static final String            TYPE_BOOLEAN           = "Boolean";
    public static final String            TYPE_DATE              = "Date";
    public static final String            TYPE_OBJECT            = "Object";                                                                                                   // for all other unknown
    public static final String            TYPE_FINAL_OBJECT      = "FinalObject";                                                                                                   // for all other unknown
    // types

    /**
     * The operators that are used at different times (based on type).
     */
    private static final String[]         STANDARD_CONNECTIVES   = new String[]{"|| ==", "|| !=", "&& !="};
    private static final String[]         STRING_CONNECTIVES     = new String[]{"|| ==", "|| !=", "&& !=", "&& matches", "|| matches"};
    private static final String[]         COMPARABLE_CONNECTIVES = new String[]{"|| ==", "|| !=", "&& !=", "&& >", "&& <", "|| >", "|| <", "&& >=", "&& <=", "|| <=", "|| >="};
    private static final String[]         COLLECTION_CONNECTIVES = new String[]{"|| ==", "|| !=", "&& !=", "|| contains", "&& contains", "|| excludes", "&& excludes"};

    private static final String[]         STANDARD_OPERATORS     = new String[]{"==", "!="};
    private static final String[]         COMPARABLE_OPERATORS   = new String[]{"==", "!=", "<", ">", "<=", ">="};
    private static final String[]         STRING_OPERATORS       = new String[]{"==", "!=", "matches", "soundslike"};
    private static final String[]         COLLECTION_OPERATORS   = new String[]{"contains", "excludes", "==", "!="};

    /** The top level conditional elements (first order logic) */
    private static final String[]         CONDITIONAL_ELEMENTS   = new String[]{"not", "exists", "or"};

   
    /**
    * A map of the field that contains the parametrized type of a collection
    * List name
    * key = "name"
    * value = "String"
    *
    */
    private Map            fieldParametersType    = new HashMap();

    /**
     * Contains a map of globals (name is key) and their type (value).
     */
    private Map            globalTypes            = new HashMap();

    /**
     * A map of types to the modifying methods they expose. key is type, value
     * is (Sting[] of modifying methods)
     * 
     **/
    private Map            modifiers;

    /**
     * Contains a map of { TypeName.field : String[] } - where a list is valid
     * values to display in a drop down for a given Type.field combination.
     */
    private Map          dataEnumLists          = new HashMap();                                                                            // TODO this is
    // a PROBLEM as
    // its not
    // always
    // String[]

    /**
     * This will show the names of globals that are a collection type.
     */
    private String[]                       globalCollections;

    /** Operators (from the grammar):
         *      op=(    '=='
         |   '>'
         |   '>='
         |   '<'
         |   '<='
         |   '!='
         |   'contains'
         |   'matches'
         |       'excludes'
         )
         * Connectives add "&" and "|" to this.
         */

    /**
     * DSL language extensions, if needed, if provided by the package.
     */
    public DSLSentence[]                  conditionDSLSentences  = new DSLSentence[0];
    public DSLSentence[]                  actionDSLSentences     = new DSLSentence[0];
    public DSLSentence[]                  keywordDSLItems        = new DSLSentence[0];
    public DSLSentence[]                  anyScopeDSLItems       = new DSLSentence[0];

    /**
     * This is used to calculate what fields an enum list may depend on.
     * Optional.
     */
    private transient Map                 dataEnumLookupFields;

    // /**
    // * For bulk loading up the data (from a previous rule save)
    // *
    // * @param factToFields A map of "FactType" (key - String) to String[]
    // (value)
    // * @param factFieldToOperator A map of "FactType.field" (key - String) to
    // String[] operators
    // * @param factFieldToConnectiveOperator A map of "FactType.field" (key
    // -String) to String[] operators
    // * that are valid CONNECTIVE operators.
    // *
    // * @param globals A map of global variable name to its fields (String[]).
    // * @param boundFacts A map of bound facts to types.
    // * @param conditionDSLs a list of DSLSentence suggestions for the LHS
    // * @param actionDSLs a list of DSLSentence suggestions for the RHS
    // *
    // */
    // public void load(
    // Map factToFields,
    // Map factFieldToOperator,
    // Map factFieldToConnectiveOperator,
    // Map globals,
    // List conditionDSLs,
    // List actionDSLs
    // ) {
    // this.factToFields = factToFields;
    // this.factFieldToOperator = factFieldToOperator;
    // this.factFieldToConnectiveOperator = factFieldToConnectiveOperator;
    // this.actionDSLSentences = actionDSLs;
    // this.conditionDSLSentences = conditionDSLs;
    // this.globals = globals;
    //
    // }

    private Map> methodInfos            = new HashMap>();

    private Map modelFields = new HashMap();
    private Map filterModelFields = null;

    private Map accessorsAndMutators = new HashMap();
	private FactTypeFilter factFilter = null;
	private boolean filteringFacts = true;
    
    public SuggestionCompletionEngine() {

    }

    public String[] getConditionalElements() {
        return CONDITIONAL_ELEMENTS;
    }

    public DSLSentence[] getDSLConditions() {
        return this.conditionDSLSentences;
    }

    public DSLSentence[] getDSLActions() {
        return this.actionDSLSentences;
    }

    public String[] getConnectiveOperatorCompletions(final String factType,
                                                     final String fieldName) {
        final String type = this.getFieldType( factType + "." + fieldName );
        if ( type == null ) {
            return STANDARD_CONNECTIVES;
        } else if ( type.equals( TYPE_STRING ) ) {
            return STRING_CONNECTIVES;
        } else if ( type.equals( TYPE_COMPARABLE ) || type.equals( TYPE_DATE ) || type.equals( TYPE_NUMERIC ) ) {
            return COMPARABLE_CONNECTIVES;
        } else if ( type.equals( TYPE_COLLECTION ) ) {
            return COLLECTION_CONNECTIVES;
        } else {
            return STANDARD_CONNECTIVES;
        }

    }

    public String[] getFieldCompletions(final String factType) {
        return this.getModelFields( factType );
    }

    public String[] getFieldCompletions(FieldAccessorsAndMutators accessorOrMutator,
                                        String factType) {
        return this.getModelFields( accessorOrMutator,
                                    factType );
    }
    
    public String[] getOperatorCompletions(final String factType,
                                           final String fieldName) {
        return getOperatorCompletions(getFieldType( factType, fieldName ));
    }

	public String[] getOperatorCompletions(final String type) {
		if (type == null) {
			return STANDARD_OPERATORS;
		} else if (type.equals(TYPE_STRING)) {
			return STRING_OPERATORS;
		} else if (type.equals(TYPE_COMPARABLE) || type.equals(TYPE_DATE) || type.equals(TYPE_NUMERIC)) {
			return COMPARABLE_OPERATORS;
		} else if (type.equals(TYPE_COLLECTION)) {
			return COLLECTION_OPERATORS;
		} else {
			return STANDARD_OPERATORS;
		}
	}
    
    public String[] getFieldCompletionsForGlobalVariable(final String varName) {
        final String type = this.getGlobalVariable( varName );
        return this.getModelFields(type);
    }

    public List getMethodInfosForGlobalVariable(final String varName) {
        final String type = this.getGlobalVariable( varName );
        return this.methodInfos.get( type );
    }

    private String[] toStringArray(final Set set) {
        final String[] f = new String[set.size()];
        int i = 0;
        for ( final Iterator iter = set.iterator(); iter.hasNext(); i++) {
            f[i] = iter.next().toString();
        }
        return f;
    }

    /**
     * This returns a list of enums options (values) that can be used for the
     * given field of the given FactPattern.
     * 
     * This also takes into account enums that depend on other fields.
     * 
     */
    public DropDownData getEnums(FactPattern pat,
                                 String field) {

    	if (field == null) {
    		return null;
    	}
        Map dataEnumLookupFields = loadDataEnumLookupFields();

        if ( pat.constraintList != null && pat.constraintList.constraints != null ) {
            // we may need to check for data dependent enums
            Object _typeFields = dataEnumLookupFields.get( pat.factType + "." + field );

            if ( _typeFields instanceof String ) {
                String typeFields = (String) _typeFields;
                FieldConstraint[] cons = pat.constraintList.constraints;

                String key = pat.factType + "." + field;

                boolean addOpeninColumn = true;
                String[] splitTypeFields = typeFields.split( "," );
                for ( int j = 0; j < splitTypeFields.length; j++ ) {
                    String typeField = splitTypeFields[j];

                    for ( int i = 0; i < cons.length; i++ ) {
                        FieldConstraint con = cons[i];
                        if ( con instanceof SingleFieldConstraint ) {
                            SingleFieldConstraint sfc = (SingleFieldConstraint) con;

                            if ( sfc.getFieldName().trim().equals( typeField.trim() ) ) {
                                if ( addOpeninColumn ) {
                                    key += "[";
                                    addOpeninColumn = false;
                                }
                                key += typeField + "=" + sfc.getValue();

                                if ( j != (splitTypeFields.length - 1) ) {
                                    key += ",";
                                }
                            }
                        }
                    }
                }

                if ( !addOpeninColumn ) {
                    key += "]";
                }

                DropDownData data = DropDownData.create( this.dataEnumLists.get( key ) );
                if ( data != null ) {
                    return DropDownData.create( this.dataEnumLists.get( key ) );
                }
            } else if ( _typeFields != null ) {
                // these enums are calculated on demand, server side...
                String[] fieldsNeeded = (String[]) _typeFields;

                String queryString = getQueryString( pat.factType,
                                                     field,
                                                     fieldsNeeded,
                                                     this.dataEnumLists );

                String[] valuePairs = new String[fieldsNeeded.length];

                // collect all the values of the fields needed, then return it
                // as a string...
                for ( int i = 0; i < fieldsNeeded.length; i++ ) {
                    for ( int j = 0; j < pat.constraintList.constraints.length; j++ ) {
                        FieldConstraint con = pat.constraintList.constraints[j];
                        if ( con instanceof SingleFieldConstraint ) {
                            SingleFieldConstraint sfc = (SingleFieldConstraint) con;
                            if ( sfc.getFieldName().equals( fieldsNeeded[i] ) ) {
                                valuePairs[i] = fieldsNeeded[i] + "=" + sfc.getValue();
                            }
                        }
                    }
                }

                if ( valuePairs.length > 0 && valuePairs[0] != null ) {
                    return DropDownData.create( queryString,
                                                valuePairs );
                }
            }
        }
        return DropDownData.create( getEnumValues( pat.factType,
                                                   field ) );
    }

    /**
     * Similar to the one above - but this one is for RHS.
     */
    public DropDownData getEnums(String type,
                                 ActionFieldValue[] currentValues,
                                 String field) {

        if ( currentValues != null ) {
            Map dataEnumLookupFields = loadDataEnumLookupFields();
            Object _typeField = dataEnumLookupFields.get( type + "." + field );

            if ( _typeField instanceof String ) {
                String typeField = (String) dataEnumLookupFields.get( type + "." + field );
                for ( int i = 0; i < currentValues.length; i++ ) {
                    ActionFieldValue val = currentValues[i];
                    if ( val.field.equals( typeField ) ) {
                        String key = type + "." + field + "[" + typeField + "=" + val.value + "]";
                        return DropDownData.create( this.getDataEnumList( key ) );
                    }
                }
            } else if ( _typeField != null ) {
                String[] fieldsNeeded = (String[]) _typeField;
                String queryString = getQueryString( type,
                                                     field,
                                                     fieldsNeeded,
                                                     this.dataEnumLists );
                String[] valuePairs = new String[fieldsNeeded.length];

                // collect all the values of the fields needed, then return it
                // as a string...
                for ( int i = 0; i < fieldsNeeded.length; i++ ) {
                    for ( int j = 0; j < currentValues.length; j++ ) {
                        ActionFieldValue con = currentValues[j];
                        if ( con.field.equals( fieldsNeeded[i] ) ) {
                            valuePairs[i] = fieldsNeeded[i] + "=" + con.value;
                        }
                    }
                }
                return DropDownData.create( queryString,
                                            valuePairs );

            }
        }

        String[] vals = this.getDataEnumList( type + "." + field );
        return DropDownData.create( vals );

    }

    /**
     * Get the query string for a fact.field It will ignore any specified field,
     * and just look for the string - as there should only be one Fact.field of
     * this type (it is all determined server side).
     * @param fieldsNeeded 
     */
    String getQueryString(String factType,
                          String field,
                          String[] fieldsNeeded,
                          Map dataEnumLists) {
        for ( Iterator iterator = dataEnumLists.keySet().iterator(); iterator.hasNext(); ) {
            String key = iterator.next();
            if ( key.startsWith( factType + "." + field ) && fieldsNeeded != null && key.contains( "[" ) ) {

                String[] values = key.substring( key.indexOf( '[' ) + 1,
                                                 key.lastIndexOf( ']' ) ).split( "," );

                if ( values.length != fieldsNeeded.length ) {
                    continue;
                }

                boolean fail = false;
                for ( int i = 0; i < values.length; i++ ) {
                    String a = values[i].trim();
                    String b = fieldsNeeded[i].trim();
                    if ( !a.equals( b ) ) {
                        fail = true;
                        break;
                    }
                }
                if ( fail ) {
                    continue;
                }

                String[] qry = getDataEnumList( key );
                return qry[0];
            } else if ( key.startsWith( factType + "." + field ) && (fieldsNeeded == null || fieldsNeeded.length == 0) ) {
                String[] qry = getDataEnumList( key );
                return qry[0];
            }
        }
        throw new IllegalStateException();
    }

    /**
     * For simple cases - where a list of values are known based on a field.
     */
    public String[] getEnumValues(String factType,
                                  String field) {
        return this.getDataEnumList( factType + "." + field );
    }

    /**
     * This is only used by enums that are like Fact.field[something=X] and so
     * on.
     */
    Map loadDataEnumLookupFields() {
        if ( this.dataEnumLookupFields == null ) {
            this.dataEnumLookupFields = new HashMap();
            Set keys = this.dataEnumLists.keySet();
            for ( Iterator iter = keys.iterator(); iter.hasNext(); ) {
                String key = iter.next();
                if ( key.indexOf( '[' ) != -1 ) {
                    int ix = key.indexOf( '[' );
                    String factField = key.substring( 0,
                                                      ix );
                    String predicate = key.substring( ix + 1,
                                                      key.indexOf( ']' ) );
                    if ( predicate.indexOf( '=' ) > -1 ) {

                        String[] bits = predicate.split( "," );
                        String typeField = "";

                        for ( int i = 0; i < bits.length; i++ ) {
                            typeField += bits[i].substring( 0,
                                                            bits[i].indexOf( '=' ) );
                            if ( i != (bits.length - 1) ) {
                                typeField += ",";
                            }
                        }

                        dataEnumLookupFields.put( factField,
                                                  typeField );
                    } else {
                        String[] fields = predicate.split( "," );
                        for ( int i = 0; i < fields.length; i++ ) {
                            fields[i] = fields[i].trim();
                        }
                        dataEnumLookupFields.put( factField,
                                                  fields );
                    }
                }
            }
        }

        return dataEnumLookupFields;
    }

    public void addMethodInfo(String factName,
                              List methodInfos) {
        this.methodInfos.put( factName,
                              methodInfos );
    }
    
    public List getMethodParams(String factName,
                                        String methodNameWithParams) {
        if ( methodInfos.get( factName ) != null ) {
            List infos = methodInfos.get( factName );

            for ( MethodInfo info : infos ) {
                if ( info.getNameWithParameters().startsWith( methodNameWithParams ) ) {
                    return info.getParams();
                }
            }
        }

        return null;
    }

    public List getMethodNames(String factName) {
        List infos = methodInfos.get( factName );
        List methodList = new ArrayList();

        if ( infos != null ) {
            for ( MethodInfo info : infos ) {
                methodList.add( info.getName() );
            }
        }

        return methodList;
    }

    public MethodInfo getMethodinfo(String factName, String methodFullName) {
    	List infos = methodInfos.get( factName );

        if ( infos != null ) {
            for ( MethodInfo info : infos ) {
                if (info.getNameWithParameters().equals(methodFullName)) {
                	return info;
                }
            }
        }

        return null;
    }
    
    public String getMethodClassType(String factName, String methodFullName) {
    	List infos = methodInfos.get( factName );

        if ( infos != null ) {
            for ( MethodInfo info : infos ) {
                if (info.getNameWithParameters().equals(methodFullName)) {
                	return info.getReturnClassType();
                }
            }
        }

        return null;
    }
    
    public List getMethodFullNames(String factName) {
        return getMethodFullNames(factName, -1);
    }    

    public List getMethodFullNames(String factName, int paramCount) {
        List infos = methodInfos.get( factName );
        List methodList = new ArrayList();

        if ( infos != null ) {
			for (MethodInfo info : infos) {
				if (paramCount == -1 || info.getParams().size() <= paramCount) {
					methodList.add(info.getNameWithParameters());
				}
			}
        }

        return methodList;
    }
    
    /**
     * Returns fact's name from class type
     *  
     * @param type
     * @return
     */
    public String getFactNameFromType(String type) {
    	if (type == null) {
    		return null;
    	}
    	if (getModelFields().containsKey(type)) {
    		return type;
    	} 
    	for (Map.Entry entry : getModelFields().entrySet()) {
			for (ModelField mf : entry.getValue()) {
				if ("this".equals(mf.getName()) && type.equals(mf.getClassName())) {
					return entry.getKey();
				}
			}
		}
    	return null;
    }
    
    /**
     * returns the type of parametric class
     * List a in a class called Toto
     * key =   "Toto.a"
     * value = "String"
     */
    public String getParametricFieldType(final String factType,
                                         final String fieldName) {
        return this.getParametricFieldType( factType + "." + fieldName );
    }

    public String getParametricFieldType(String fieldName){
        return this.fieldParametersType.get(fieldName);
    }

    public void putParametricFieldType(String fieldName, String type){
        this.fieldParametersType.put(fieldName, type);
    }
    
    public String getGlobalVariable(String name){
        return this.globalTypes.get(name);
    }

    public boolean isGlobalVariable(String name){
        return this.globalTypes.containsKey(name);
    }

    public void setGlobalVariables(Map globalTypes){
         this.globalTypes = globalTypes;
    }

    public String[] getGlobalVariables() {
        return toStringArray( this.globalTypes.keySet() );
    }

    public void setModifiers(Map map){
        this.modifiers = map;
    }

    public String[] getModifiers(String name){
        return this.modifiers.get(name);
    }

    public void setGlobalCollections(String[] globalCollections){
        this.globalCollections = globalCollections;
    }

    public String[] getGlobalCollections() {
        return this.globalCollections;
    }

    public String[] getDataEnumList(String type){
        return this.dataEnumLists.get(type);
    }

    public void setDataEnumLists(Map data){
        this.dataEnumLists = data;
    }

    public void putDataEnumList(String name,String[] value){
        this.dataEnumLists.put(name, value);
    }

    public void putAllDataEnumLists(Map value){
        this.dataEnumLists.putAll(value);
    }

    public int getDataEnumListsSize(){
        return this.dataEnumLists.size();
    }

    public boolean hasDataEnumLists(){
        return this.dataEnumLists != null && this.dataEnumLists.size() > 0;
    }


    ////

    public void setFactTypes(String[] factTypes) {
        for (String factType : factTypes) {
            //adds the fact type with no fields.
            this.getModelFields().put(factType, new ModelField[0]);
        }
    }

    public void setFactTypeFilter(FactTypeFilter filter){
    	this.factFilter = filter;
    	filterModelFields();
    }

    public void setFieldsForTypes(Map fieldsForType){
    	this.getModelFields().clear();
        this.getModelFields().putAll(fieldsForType);
    }

    /**
     * Returns all the fact types.
     * @return
     */
    public String[] getFactTypes() {
        String[] types = this.getModelFields().keySet().toArray(new String[this.getModelFields().size()]);
        Arrays.sort(types);
		return types;
    }

    public boolean containsFactType(String modelClassName){
        if (modelClassName.contains(".")){
            modelClassName = modelClassName.substring(modelClassName.lastIndexOf(".")+1);
        }
        return this.getModelFields().containsKey(modelClassName);
    }

    private ModelField getField(String modelClassName, String fieldName){

        String shortName = this.getFactNameFromType(modelClassName );

        ModelField[] fields = this.getModelFields().get(shortName);

        if (fields == null){
            return null;
        }

        for (ModelField modelField : fields) {
            if (modelField.getName().equals(fieldName)){
                return modelField;
            }
        }

        return null;
    }

    public String[] getModelFields(FieldAccessorsAndMutators accessorOrMutator,
                                   String modelClassName) {

        String shortName = this.getFactNameFromType(modelClassName );

        if ( !this.getModelFields().containsKey( shortName) ) {
            return new String[0];
        }

        ModelField[] fields = this.getModelFields().get( shortName );

        List fieldNames = new ArrayList();
        fieldNames.add( "this" );

        for ( int i = 0; i < fields.length; i++ ) {
            String fieldName = fields[i].getName();
            if ( fields[i].getClassType() == FIELD_CLASS_TYPE.TYPE_DECLARATION_CLASS ) {
                fieldNames.add( fieldName );
            } else if ( FieldAccessorsAndMutators.compare( accessorOrMutator,
                                                           this.accessorsAndMutators.get( shortName + "." + fieldName ) ) ) {
                fieldNames.add( fieldName );
            }
        }

        return fieldNames.toArray( new String[fieldNames.size()] );
    }

    public String[] getModelFields(String modelClassName){

        String shortName = this.getFactNameFromType(modelClassName );

        if (!this.getModelFields().containsKey(shortName)){
            return new String[0];
        }

        ModelField[] fields = this.getModelFields().get(shortName);

        String[] fieldNames = new String[fields.length];

        for (int i=0;i accessorsAndMutators) {
        this.accessorsAndMutators=accessorsAndMutators;
    }

    
    
	public void setModelFields(Map modelFields) {
		this.modelFields = modelFields;
		filterModelFields();
	}

	private void filterModelFields() {
		if (factFilter != null) {
			filterModelFields = new HashMap();
			for (Map.Entry entry : modelFields.entrySet()) {
				if (!factFilter.filter(entry.getKey())) {
					filterModelFields.put(entry.getKey(), entry.getValue());
				}
			}
		}
	}
	
	public Map getModelFields() {
		if (factFilter != null && isFilteringFacts()) {
			return filterModelFields;
		}
		return modelFields;
	}

	public boolean isFilteringFacts() {
		return filteringFacts;
	}

	public void setFilteringFacts(boolean filterFacts) {
		this.filteringFacts = filterFacts;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy