org.drools.ide.common.client.modeldriven.SuggestionCompletionEngine Maven / Gradle / Ivy
/**
* 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