/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kie.workbench.common.widgets.client.datamodel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;
import org.drools.workbench.models.datamodel.imports.Imports;
import org.drools.workbench.models.datamodel.oracle.Annotation;
import org.drools.workbench.models.datamodel.oracle.DataType;
import org.drools.workbench.models.datamodel.oracle.DropDownData;
import org.drools.workbench.models.datamodel.oracle.FieldAccessorsAndMutators;
import org.drools.workbench.models.datamodel.oracle.MethodInfo;
import org.drools.workbench.models.datamodel.oracle.ModelField;
import org.drools.workbench.models.datamodel.oracle.OperatorsOracle;
import org.drools.workbench.models.datamodel.oracle.TypeSource;
import org.drools.workbench.models.datamodel.rule.DSLSentence;
import org.jboss.errai.common.client.api.Caller;
import org.jboss.errai.common.client.api.RemoteCallback;
import org.jboss.errai.validation.client.dynamic.DynamicValidator;
import org.kie.workbench.common.services.datamodel.model.LazyModelField;
import org.kie.workbench.common.services.datamodel.model.PackageDataModelOracleIncrementalPayload;
import org.kie.workbench.common.services.datamodel.service.IncrementalDataModelService;
import org.kie.workbench.common.services.datamodel.util.SortHelper;
import org.uberfire.backend.vfs.Path;
import org.uberfire.client.callbacks.Callback;
import org.uberfire.commons.validation.PortablePreconditions;
/**
* Default implementation of DataModelOracle
*/
@Dependent
public class AsyncPackageDataModelOracleImpl implements AsyncPackageDataModelOracle {
private Caller<IncrementalDataModelService> service;
private Instance<DynamicValidator> validatorInstance;
private DynamicValidator validator;
//Path that this DMO is coupled to
private Path resourcePath;
//Project name
protected String projectName;
//Package for which this DMO relates
private String packageName = "";
//Imports from the Project into this Package
private Imports imports = new Imports();
// List of available package names
private List<String> packageNames = new ArrayList<String>();
// ####################################
// Project Scope
// ####################################
//Fact Types and their corresponding fields
protected Map<String, ModelField[]> projectModelFields = new TreeMap<String, ModelField[]>( SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
//Map of the field that contains the parametrized type of a collection
//for example given "List<String> name", key = "name" value = "String"
protected Map<String, String> projectFieldParametersType = new HashMap<String, String>();
//Map {factType, isEvent} to determine which Fact Type can be treated as events.
protected Map<String, Boolean> projectEventTypes = new HashMap<String, Boolean>();
//Map {factType, TypeSource} to determine where a Fact Type as defined.
protected Map<String, TypeSource> projectTypeSources = new HashMap<String, TypeSource>();
//Map {factType, superType} to determine the Super Type of a FactType.
protected Map<String, List<String>> projectSuperTypes = new HashMap<String, List<String>>();
//Map {factType, Set<Annotation>} containing the FactType's annotations.
protected Map<String, Set<Annotation>> projectTypeAnnotations = new HashMap<String, Set<Annotation>>();
//Map {factType, Map<fieldName, Set<Annotation>>} containing the FactType's Field annotations.
protected Map<String, Map<String, Set<Annotation>>> projectTypeFieldsAnnotations = new HashMap<String, Map<String, Set<Annotation>>>();
// Scoped (current package and imports) map of { TypeName.field : String[] } - where a list is valid values to display in a drop down for a given Type.field combination.
protected Map<String, String[]> projectJavaEnumLists = new HashMap<String, String[]>();
//Method information used (exclusively) by ExpressionWidget and ActionCallMethodWidget
protected Map<String, List<MethodInfo>> projectMethodInformation = new HashMap<String, List<MethodInfo>>();
// A map of FactTypes {factType, isCollection} to determine which Fact Types are Collections.
protected Map<String, Boolean> projectCollectionTypes = new HashMap<String, Boolean>();
// ####################################
// Package Scope
// ####################################
// Filtered (current package and imports) Fact Types and their corresponding fields
private Map<String, ModelField[]> filteredModelFields = new TreeMap<String, ModelField[]>( SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
// Filtered (current package and imports) map of the field that contains the parametrized type of a collection
// for example given "List<String> name", key = "name" value = "String"
private Map<String, String> filteredFieldParametersType = new HashMap<String, String>();
// Filtered (current package and imports) map of { TypeName.field : String[] } - where a list is valid values to display in a drop down for a given Type.field combination.
private FilteredEnumLists filteredEnumLists = new FilteredEnumLists();
// Filtered (current package and imports) Map {factType, isEvent} to determine which Fact Type can be treated as events.
private Map<String, Boolean> filteredEventTypes = new HashMap<String, Boolean>();
// Filtered (current package and imports) Map {factType, isCollection} to determine which Fact Types are Collections.
private Map<String, Boolean> filteredCollectionTypes = new HashMap<String, Boolean>();
// Filtered (current package and imports) Map {factType, TypeSource} to determine where a Fact Type as defined.
private Map<String, TypeSource> filteredTypeSources = new HashMap<String, TypeSource>();
// Filtered (current package and imports) Map {factType, superType} to determine the Super Type of a FactType.
protected Map<String, List<String>> filteredSuperTypes = new HashMap<String, List<String>>();
// Filtered (current package and imports) Map {factType, Set<Annotation>} containing the FactType's annotations.
protected Map<String, Set<Annotation>> filteredTypeAnnotations = new HashMap<String, Set<Annotation>>();
// Filtered (current package and imports) Map {factType, {fieldName, Set<Annotation>}} containing the FactType's Fields annotations.
protected Map<String, Map<String, Set<Annotation>>> filteredTypeFieldsAnnotations = new HashMap<String, Map<String, Set<Annotation>>>();
// Filtered (current package and imports) map of Globals {alias, class name}.
private Map<String, String> filteredGlobalTypes = new TreeMap<String, String>( SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
// Package-level enumeration definitions derived from "Workbench" enumerations.
private Map<String, String[]> packageWorkbenchEnumLists = new HashMap<String, String[]>();
// Package-level DSL language extensions.
private List<DSLSentence> packageDSLConditionSentences = new ArrayList<DSLSentence>();
private List<DSLSentence> packageDSLActionSentences = new ArrayList<DSLSentence>();
// Package-level map of Globals {alias, class name}.
private Map<String, String> packageGlobalTypes = new HashMap<String, String>();
// Keep the link between fact name and the full qualified class name inside the package
private FactNameToFQCNHandleRegistry factNameToFQCNHandleRegistry = new FactNameToFQCNHandleRegistry();
@Inject
public AsyncPackageDataModelOracleImpl( final Caller<IncrementalDataModelService> service,
final Instance<DynamicValidator> validatorInstance ) {
this.service = service;
this.validatorInstance = validatorInstance;
}
public Map<String, ModelField[]> getFilteredFactTypes() {
return filteredModelFields;
}
@Override
public void init( final Path resourcePath ) {
this.resourcePath = PortablePreconditions.checkNotNull( "resourcePath",
resourcePath );
}
@Override
public Path getResourcePath() {
return this.resourcePath;
}
// ####################################
// Packages
// ####################################
@Override
public List<String> getPackageNames() {
return packageNames;
}
// ####################################
// Fact Types
// ####################################
/**
* Returns fact types available for rule authoring, i.e. those within the same package and those that have been imported.
* @return
*/
@Override
public String[] getFactTypes() {
final String[] types = filteredModelFields.keySet().toArray( new String[ filteredModelFields.size() ] );
return types;
}
/**
* Return all fact types available to the project, i.e. everything type defined within the project or externally imported
* @return
*/
@Override
public String[] getAllFactTypes() {
final List<String> types = new ArrayList<String>();
types.addAll( this.projectModelFields.keySet() );
final String[] result = new String[ types.size() ];
types.toArray( result );
return result;
}
/**
* Return all fact types that are internal to the package, i.e. they do not need to be imported to be used
* @return
*/
@Override
public String[] getInternalFactTypes() {
final String[] allTypes = getAllFactTypes();
final List<String> internalTypes = new ArrayList<String>();
for ( String type : allTypes ) {
final String packageName = AsyncPackageDataModelOracleUtilities.getPackageName( type );
if ( packageName.equals( this.packageName ) ) {
internalTypes.add( type );
}
}
final String[] result = new String[ internalTypes.size() ];
internalTypes.toArray( result );
return result;
}
/**
* Return all fact types that are external to the package, i.e. they need to be imported to be used
* @return
*/
@Override
public String[] getExternalFactTypes() {
final String[] allTypes = getAllFactTypes();
final List<String> externalTypes = new ArrayList<String>();
for ( String type : allTypes ) {
final String packageName = AsyncPackageDataModelOracleUtilities.getPackageName( type );
if ( !packageName.equals( this.packageName ) ) {
externalTypes.add( type );
}
}
final String[] result = new String[ externalTypes.size() ];
externalTypes.toArray( result );
return result;
}
public String getFQCNByFactName( final String factName ) {
if ( factNameToFQCNHandleRegistry.contains( factName ) ) {
return factNameToFQCNHandleRegistry.get( factName );
} else if ( factName.contains( "." ) ) {
return factName;
} else {
return factName;
}
}
/**
* Returns fact's name from type
* @param type for example org.test.Person or Person
* @return Shorter type name Person, not org.test.Person
*/
@Override
public String getFactNameFromType( final String type ) {
if ( type == null || type.isEmpty() ) {
return null;
}
if ( filteredModelFields.containsKey( type ) ) {
return type;
}
for ( Map.Entry<String, ModelField[]> entry : filteredModelFields.entrySet() ) {
for ( ModelField mf : entry.getValue() ) {
if ( DataType.TYPE_THIS.equals( mf.getName() ) && type.equals( mf.getClassName() ) ) {
return entry.getKey();
}
}
}
final String fgcnByFactName = getFQCNByFactName( type );
if ( projectModelFields.containsKey( fgcnByFactName ) ) {
return AsyncPackageDataModelOracleUtilities.getTypeName( fgcnByFactName );
}
return null;
}
/**
* Is the Fact Type known to the DataModelOracle
* @param factType
* @return
*/
@Override
public boolean isFactTypeRecognized( final String factType ) {
if ( filteredModelFields.containsKey( factType ) || factNameToFQCNHandleRegistry.map.containsValue( factType ) ) {
return true;
} else {
return false;
}
}
/**
* Check whether a given FactType is an Event for CEP purposes
* @param factType
* @return
*/
@Override
public void isFactTypeAnEvent( final String factType,
final Callback<Boolean> callback ) {
if ( factType == null || factType.isEmpty() ) {
callback.callback( false );
return;
}
final Boolean isFactTypeAnEvent = filteredEventTypes.get( factType );
//Load incremental content
if ( isFactTypeAnEvent == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
Boolean isFactTypeAnEvent = filteredEventTypes.get( factType );
if ( isFactTypeAnEvent == null ) {
isFactTypeAnEvent = false;
filteredEventTypes.put( factType,
isFactTypeAnEvent );
}
callback.callback( isFactTypeAnEvent );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( isFactTypeAnEvent );
}
}
/**
* Return where a given FactType was defined
* @param factType
* @return
*/
@Override
public void getTypeSource( final String factType,
final Callback<TypeSource> callback ) {
final TypeSource typeSource = filteredTypeSources.get( factType );
//Load incremental content
if ( typeSource == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
final TypeSource typeSource = filteredTypeSources.get( factType );
callback.callback( typeSource );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( typeSource );
}
}
/**
* Get the Super Type for a given FactType
* @param factType
* @return null if no Super Type
*/
@Override
public void getSuperType( final String factType,
final Callback<String> callback ) {
getSuperTypes( factType,
new Callback<List<String>>() {
@Override
public void callback( List<String> result ) {
if ( result != null ) {
callback.callback( result.get( 0 ) );
} else {
callback.callback( null );
}
}
} );
}
@Override
public void getSuperTypes( final String factType,
final Callback<List<String>> callback ) {
final List<String> superTypes = filteredSuperTypes.get( factType );
//Load incremental content
if ( superTypes == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
callback.callback( filteredSuperTypes.get( factType ) );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( superTypes );
}
}
/**
* Get the Annotations for a given FactType
* @param factType
* @return Empty set if no annotations exist for the type
*/
@Override
public void getTypeAnnotations( final String factType,
final Callback<Set<Annotation>> callback ) {
final Set<Annotation> typeAnnotations = filteredTypeAnnotations.get( factType );
//Load incremental content
if ( typeAnnotations == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
Set<Annotation> typeAnnotations = filteredTypeAnnotations.get( factType );
if ( typeAnnotations == null ) {
typeAnnotations = Collections.EMPTY_SET;
filteredTypeAnnotations.put( factType,
typeAnnotations );
}
callback.callback( typeAnnotations );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( typeAnnotations );
}
}
/**
* Get the Fields Annotations for a given FactType
* @param factType
* @return Empty Map if no annotations exist for the type
*/
@Override
public void getTypeFieldsAnnotations( final String factType,
final Callback<Map<String, Set<Annotation>>> callback ) {
final Map<String, Set<Annotation>> typeFieldsAnnotations = filteredTypeFieldsAnnotations.get( factType );
//Load incremental content
if ( typeFieldsAnnotations == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
Map<String, Set<Annotation>> typeFieldsAnnotations = filteredTypeFieldsAnnotations.get( factType );
if ( typeFieldsAnnotations == null ) {
typeFieldsAnnotations = Collections.EMPTY_MAP;
filteredTypeFieldsAnnotations.put( factType,
typeFieldsAnnotations );
}
callback.callback( typeFieldsAnnotations );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( typeFieldsAnnotations );
}
}
@Override
public <T> void validateField( final String factType,
final String fieldName,
final T value,
final Callback<Set<ConstraintViolation<T>>> callback ) {
if ( factType == null || factType.isEmpty() ) {
callback.callback( Collections.emptySet() );
return;
}
if ( fieldName == null || fieldName.isEmpty() ) {
callback.callback( Collections.emptySet() );
return;
}
if ( callback == null ) {
return;
}
if ( validatorInstance.isUnsatisfied() ) {
callback.callback( Collections.emptySet() );
return;
} else if ( validator == null ) {
validator = validatorInstance.get();
}
getTypeFieldsAnnotations( factType,
( Map<String, Set<Annotation>> result ) -> {
final Set<ConstraintViolation<T>> violations = new HashSet<>();
final Set<Annotation> fieldAnnotations = result.get( fieldName );
if ( fieldAnnotations == null || fieldAnnotations.isEmpty() ) {
callback.callback( violations );
return;
}
for ( Annotation fieldAnnotation : fieldAnnotations ) {
final Map<String, Object> fieldAnnotationAttributes = fieldAnnotation.getParameters();
violations.addAll( validator.validate( fieldAnnotation.getQualifiedTypeName(),
fieldAnnotationAttributes,
value ) );
}
callback.callback( violations );
} );
}
// ####################################
// Fact Types' Fields
// ####################################
@Override
public void getFieldCompletions( final String factType,
final Callback<ModelField[]> callback ) {
final String fgcnByFactName = getFQCNByFactName( factType );
ModelField[] fields = getModelFields( factType );
if ( fields == null || fields.length == 0 ) {
fields = projectModelFields.get( fgcnByFactName );
if ( fields == null || isLazyProxy( fields ) ) {
fields = null;
} else {
AsyncPackageDataModelOracleUtilities.correctModelFields( packageName,
fields,
imports );
}
}
//Load incremental content
if ( fields == null || fields.length == 0 ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
// This will stop an infinite loop if there are no fields to be found
if ( dataModel.getModelFields().get( fgcnByFactName ) == null || dataModel.getModelFields().get( fgcnByFactName ).length == 0 ) {
callback.callback( new ModelField[ 0 ] );
} else {
getFieldCompletions( factType,
callback );
}
}
} ).getUpdates( resourcePath,
imports,
fgcnByFactName );
} else {
callback.callback( fields );
}
}
private ModelField[] getModelFields( final String modelClassName ) {
final String shortName = getFactNameFromType( modelClassName );
if ( !filteredModelFields.containsKey( shortName ) ) {
return new ModelField[ 0 ];
}
//If fields do not exist return null; so they can be incrementally loaded
final ModelField[] fields = filteredModelFields.get( shortName );
if ( isLazyProxy( fields ) ) {
return null;
}
//Otherwise return existing fields
return fields;
}
@Override
public void getFieldCompletions( final String factType,
final FieldAccessorsAndMutators accessorOrMutator,
final Callback<ModelField[]> callback ) {
getFieldCompletions( factType,
new Callback<ModelField[]>() {
@Override
public void callback( ModelField[] fields ) {
ArrayList<ModelField> result = new ArrayList<ModelField>();
for ( ModelField field : fields ) {
if ( FieldAccessorsAndMutators.compare( accessorOrMutator, field.getAccessorsAndMutators() ) ) {
result.add( field );
}
}
callback.callback( result.toArray( new ModelField[ result.size() ] ) );
}
} );
}
//Check whether the ModelField[] is a place-holder for more information
private boolean isLazyProxy( final ModelField[] modelFields ) {
if ( modelFields == null ) {
return false;
} else if ( modelFields.length != 1 ) {
return false;
}
return modelFields[ 0 ] instanceof LazyModelField;
}
@Override
public String getFieldType( final String modelClassName,
final String fieldName ) {
//Check fields
final ModelField field = getField( modelClassName,
fieldName );
if ( field != null ) {
return field.getType();
}
//Check method information
final String fgcnModelClassName = getFQCNByFactName( modelClassName );
final List<MethodInfo> mis = projectMethodInformation.get( fgcnModelClassName );
if ( mis != null ) {
for ( MethodInfo mi : mis ) {
if ( mi.getName().equals( fieldName ) ) {
return mi.getGenericType();
}
}
}
return null;
}
@Override
public String getFieldClassName( final String modelClassName,
final String fieldName ) {
//Check fields
final ModelField field = getField( modelClassName,
fieldName );
if ( field != null ) {
return field.getClassName();
}
//Check method information
final String fgcnModelClassName = getFQCNByFactName( modelClassName );
final List<MethodInfo> mis = projectMethodInformation.get( fgcnModelClassName );
if ( mis != null ) {
for ( MethodInfo mi : mis ) {
if ( mi.getName().equals( fieldName ) ) {
return mi.getReturnClassType();
}
}
}
return null;
}
private ModelField getField( final String modelClassName,
final String fieldName ) {
final String fgcnByFactName = getFQCNByFactName( modelClassName );
final ModelField[] fields = projectModelFields.get( fgcnByFactName );
if ( fields == null ) {
return null;
}
for ( ModelField modelField : fields ) {
if ( modelField.getName().equals( fieldName ) ) {
return AsyncPackageDataModelOracleUtilities.correctModelFields( packageName,
imports,
modelField );
}
}
return null;
}
/**
* Get the parametric type of a Field.
* @param factType
* @param fieldName
* @return
*/
@Override
public String getParametricFieldType( final String factType,
final String fieldName ) {
final String qualifiedFactFieldName = factType + "#" + fieldName;
return filteredFieldParametersType.get( qualifiedFactFieldName );
}
// ####################################
// Operators
// ####################################
/**
* Get the Operators applicable Base Constraints
* @param factType
* @param fieldName
* @return
*/
@Override
public void getOperatorCompletions( final String factType,
final String fieldName,
final Callback<String[]> callback ) {
final String fieldType = getFieldType( factType,
fieldName );
if ( fieldType == null ) {
callback.callback( OperatorsOracle.STANDARD_OPERATORS );
return;
} else if ( fieldName.equals( DataType.TYPE_THIS ) ) {
isFactTypeAnEvent( factType,
new Callback<Boolean>() {
@Override
public void callback( final Boolean isFactTypeAnEvent ) {
if ( Boolean.TRUE.equals( isFactTypeAnEvent ) ) {
callback.callback( OracleUtils.joinArrays( OperatorsOracle.STANDARD_OPERATORS,
OperatorsOracle.SIMPLE_CEP_OPERATORS,
OperatorsOracle.COMPLEX_CEP_OPERATORS ) );
return;
} else {
callback.callback( OperatorsOracle.STANDARD_OPERATORS );
return;
}
}
} );
} else if ( fieldType.equals( DataType.TYPE_STRING ) ) {
callback.callback( OracleUtils.joinArrays( OperatorsOracle.STRING_OPERATORS,
OperatorsOracle.EXPLICIT_LIST_OPERATORS ) );
return;
} else if ( DataType.isNumeric( fieldType ) ) {
callback.callback( OracleUtils.joinArrays( OperatorsOracle.COMPARABLE_OPERATORS,
OperatorsOracle.EXPLICIT_LIST_OPERATORS ) );
return;
} else if ( fieldType.equals( DataType.TYPE_DATE ) ) {
callback.callback( OracleUtils.joinArrays( OperatorsOracle.COMPARABLE_OPERATORS,
OperatorsOracle.EXPLICIT_LIST_OPERATORS,
OperatorsOracle.SIMPLE_CEP_OPERATORS ) );
return;
} else if ( fieldType.equals( DataType.TYPE_COMPARABLE ) ) {
callback.callback( OperatorsOracle.COMPARABLE_OPERATORS );
return;
} else if ( fieldType.equals( DataType.TYPE_COLLECTION ) ) {
callback.callback( OperatorsOracle.COLLECTION_OPERATORS );
return;
} else {
callback.callback( OperatorsOracle.STANDARD_OPERATORS );
}
}
/**
* Get the Operators applicable for Connective Constraints
* @param factType
* @param fieldName
* @return
*/
@Override
public void getConnectiveOperatorCompletions( final String factType,
final String fieldName,
final Callback<String[]> callback ) {
final String fieldType = getFieldType( factType,
fieldName );
if ( fieldType == null ) {
callback.callback( OperatorsOracle.STANDARD_CONNECTIVES );
return;
} else if ( fieldName.equals( DataType.TYPE_THIS ) ) {
isFactTypeAnEvent( factType,
new Callback<Boolean>() {
@Override
public void callback( final Boolean isFactTypeAnEvent ) {
if ( Boolean.TRUE.equals( isFactTypeAnEvent ) ) {
callback.callback( OracleUtils.joinArrays( OperatorsOracle.STANDARD_CONNECTIVES,
OperatorsOracle.SIMPLE_CEP_CONNECTIVES,
OperatorsOracle.COMPLEX_CEP_CONNECTIVES ) );
return;
} else {
callback.callback( OperatorsOracle.STANDARD_CONNECTIVES );
return;
}
}
} );
} else if ( fieldType.equals( DataType.TYPE_STRING ) ) {
callback.callback( OperatorsOracle.STRING_CONNECTIVES );
return;
} else if ( DataType.isNumeric( fieldType ) ) {
callback.callback( OperatorsOracle.COMPARABLE_CONNECTIVES );
return;
} else if ( fieldType.equals( DataType.TYPE_DATE ) ) {
callback.callback( OracleUtils.joinArrays( OperatorsOracle.COMPARABLE_CONNECTIVES,
OperatorsOracle.SIMPLE_CEP_CONNECTIVES ) );
return;
} else if ( fieldType.equals( DataType.TYPE_COMPARABLE ) ) {
callback.callback( OperatorsOracle.COMPARABLE_CONNECTIVES );
return;
} else if ( fieldType.equals( DataType.TYPE_COLLECTION ) ) {
callback.callback( OperatorsOracle.COLLECTION_CONNECTIVES );
return;
} else {
callback.callback( OperatorsOracle.STANDARD_CONNECTIVES );
}
}
// ####################################
// Methods
// ####################################
/**
* Get a list of MethodInfos for a Fact Type
* @param factType
* @param callback
* @return
*/
@Override
public void getMethodInfos( final String factType,
final Callback<List<MethodInfo>> callback ) {
getMethodInfos( factType,
-1,
callback );
}
/**
* Get a list of MethodInfos for a Fact Type that have at least the specified number of parameters
* @param factType
* @param parameterCount
* @param callback
* @return
*/
@Override
public void getMethodInfos( final String factType,
final int parameterCount,
final Callback<List<MethodInfo>> callback ) {
final String fqcnByFactName = getFQCNByFactName( factType );
final List<MethodInfo> methodInformation = projectMethodInformation.get( fqcnByFactName );
//Load incremental content
if ( methodInformation == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
final List<MethodInfo> methodInformation = projectMethodInformation.get( fqcnByFactName );
callback.callback( getMethodInfos( parameterCount,
methodInformation ) );
}
} ).getUpdates( resourcePath,
imports,
fqcnByFactName );
} else {
callback.callback( getMethodInfos( parameterCount,
methodInformation ) );
}
}
private List<MethodInfo> getMethodInfos( final int paramCount,
final List<MethodInfo> allMethodInfos ) {
final List<MethodInfo> methodInfos = new ArrayList<MethodInfo>();
if ( allMethodInfos == null ) {
return methodInfos;
}
for ( MethodInfo mi : allMethodInfos ) {
if ( paramCount == -1 || mi.getParams().size() <= paramCount ) {
methodInfos.add( mi );
}
}
return AsyncPackageDataModelOracleUtilities.correctMethodInformation( packageName,
methodInfos,
imports );
}
/**
* Get a list of parameters for a Method of a Fact Type
* @param factType
* @param methodNameWithParams
* @return
*/
@Override
public void getMethodParams( final String factType,
final String methodNameWithParams,
final Callback<List<String>> callback ) {
final String fqcnFactName = getFQCNByFactName( factType );
final List<MethodInfo> methodInformation = projectMethodInformation.get( fqcnFactName );
//Load incremental content
if ( methodInformation == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
final List<MethodInfo> methodInformation = projectMethodInformation.get( fqcnFactName );
callback.callback( getMethodParams( methodInformation,
methodNameWithParams ) );
}
} ).getUpdates( resourcePath,
imports,
fqcnFactName );
} else {
callback.callback( getMethodParams( methodInformation,
methodNameWithParams ) );
}
}
private List<String> getMethodParams( final List<MethodInfo> methodInfos,
final String methodNameWithParams ) {
final List<String> methodParams = new ArrayList<String>();
for ( MethodInfo methodInfo : methodInfos ) {
if ( methodInfo.getNameWithParameters().startsWith( methodNameWithParams ) ) {
methodParams.addAll( methodInfo.getParams() );
}
}
return methodParams;
}
/**
* Get information on a Method of a Fact Type
* @param factType
* @param methodNameWithParams
* @return
*/
@Override
public void getMethodInfo( final String factType,
final String methodNameWithParams,
final Callback<MethodInfo> callback ) {
final String fqcnFactName = getFQCNByFactName( factType );
final List<MethodInfo> methodInformation = projectMethodInformation.get( fqcnFactName );
//Load incremental content
if ( methodInformation == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
final List<MethodInfo> methodInformation = projectMethodInformation.get( fqcnFactName );
callback.callback( getMethodInfo( methodInformation,
methodNameWithParams ) );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( getMethodInfo( methodInformation,
methodNameWithParams ) );
}
}
private MethodInfo getMethodInfo( final List<MethodInfo> methodInfos,
final String methodNameWithParams ) {
for ( MethodInfo methodInfo : methodInfos ) {
if ( methodInfo.getNameWithParameters().equals( methodNameWithParams ) ) {
return AsyncPackageDataModelOracleUtilities.correctMethodInformation( packageName,
methodInfo,
imports );
}
}
return null;
}
// ####################################
// Globals
// ####################################
@Override
public String[] getGlobalVariables() {
return OracleUtils.toStringArray( filteredGlobalTypes.keySet() );
}
@Override
public String getGlobalVariable( final String name ) {
return filteredGlobalTypes.get( name );
}
@Override
public boolean isGlobalVariable( final String name ) {
return filteredGlobalTypes.containsKey( name );
}
@Override
public void getFieldCompletionsForGlobalVariable( final String varName,
final Callback<ModelField[]> callback ) {
getFieldCompletions(
getGlobalVariable( varName ),
new Callback<ModelField[]>() {
@Override
public void callback( ModelField[] result ) {
callback.callback( result );
}
} );
}
@Override
public void getMethodInfosForGlobalVariable( final String varName,
final Callback<List<MethodInfo>> callback ) {
final String factType = packageGlobalTypes.get( varName );
final List<MethodInfo> methodInformation = projectMethodInformation.get( factType );
//Load incremental content
if ( methodInformation == null ) {
service.call( new RemoteCallback<PackageDataModelOracleIncrementalPayload>() {
@Override
public void callback( final PackageDataModelOracleIncrementalPayload dataModel ) {
AsyncPackageDataModelOracleUtilities.populateDataModelOracle( AsyncPackageDataModelOracleImpl.this,
dataModel );
callback.callback( projectMethodInformation.get( factType ) );
}
} ).getUpdates( resourcePath,
imports,
factType );
} else {
callback.callback( methodInformation );
}
}
@Override
public String[] getGlobalCollections() {
final List<String> globalCollections = new ArrayList<String>();
for ( Map.Entry<String, String> e : filteredGlobalTypes.entrySet() ) {
if ( filteredCollectionTypes.containsKey( e.getValue() ) ) {
if ( Boolean.TRUE.equals( filteredCollectionTypes.get( e.getValue() ) ) ) {
globalCollections.add( e.getKey() );
}
}
}
return OracleUtils.toStringArray( globalCollections );
}
// ####################################
// DSLs
// ####################################
@Override
public List<DSLSentence> getDSLConditions() {
return Collections.unmodifiableList( packageDSLConditionSentences );
}
@Override
public List<DSLSentence> getDSLActions() {
return Collections.unmodifiableList( packageDSLActionSentences );
}
// ####################################
// Enums
// ####################################
/**
* Get enums for a Type and Field.
*/
@Override
public DropDownData getEnums( final String type,
final String field ) {
return getEnums( type,
field,
new HashMap<String, String>() );
}
/**
* Get enums for a Type and Field where the enum list may depend upon the values of other fields.
*/
@Override
public DropDownData getEnums( final String type,
final String field,
final Map<String, String> currentValueMap ) {
return new EnumDropDownDataFactory( filteredEnumLists, currentValueMap ).getEnums( type, field );
}
@Override
public String[] getEnumValues( String factType,
String factField ) {
return filteredEnumLists.getEnumValues( factType, factField );
}
@Override
public boolean hasEnums( final String factType,
final String field ) {
return hasEnums( factType + "#" + field );
}
@Override
public boolean hasEnums( final String qualifiedFactField ) {
return filteredEnumLists.hasEnums( qualifiedFactField );
}
/**
* Check whether the childField is related to the parentField through a
* chain of enumeration dependencies. Both fields belong to the same Fact
* Type. Furthermore code consuming this function should ensure both
* parentField and childField relate to the same Fact Pattern
* @param factType
* @param parentField
* @param childField
* @return
*/
@Override
public boolean isDependentEnum( final String factType,
final String parentField,
final String childField ) {
return filteredEnumLists.isDependentEnum( factType, parentField, childField );
}
// ####################################
// Imports
// ####################################
@Override
public void filter( final Imports imports ) {
this.imports = imports;
filter();
}
@Override
public void filter() {
//Filter and rename Model Fields based on package name and imports
filteredModelFields = new TreeMap<String, ModelField[]>( SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
filteredModelFields.putAll( AsyncPackageDataModelOracleUtilities.filterModelFields( packageName,
imports,
projectModelFields,
factNameToFQCNHandleRegistry ) );
// For filling the factNameToFQCNHandleRegistry
AsyncPackageDataModelOracleUtilities.visitMethodInformation( projectMethodInformation,
factNameToFQCNHandleRegistry );
//Filter and rename Global Types based on package name and imports
filteredGlobalTypes = new TreeMap<String, String>( SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
filteredGlobalTypes.putAll( AsyncPackageDataModelOracleUtilities.filterGlobalTypes( packageName,
imports,
packageGlobalTypes ) );
//Filter and rename Collection Types based on package name and imports
filteredCollectionTypes = new HashMap<String, Boolean>();
filteredCollectionTypes.putAll( AsyncPackageDataModelOracleUtilities.filterCollectionTypes( packageName,
imports,
projectCollectionTypes ) );
//Filter and rename Event Types based on package name and imports
filteredEventTypes = new HashMap<String, Boolean>();
filteredEventTypes.putAll( AsyncPackageDataModelOracleUtilities.filterEventTypes( packageName,
imports,
projectEventTypes ) );
//Filter and rename TypeSources based on package name and imports
filteredTypeSources = new HashMap<String, TypeSource>();
filteredTypeSources.putAll( AsyncPackageDataModelOracleUtilities.filterTypeSources( packageName,
imports,
projectTypeSources ) );
//Filter and rename Declared Types based on package name and imports
filteredSuperTypes = new HashMap<String, List<String>>();
filteredSuperTypes.putAll( AsyncPackageDataModelOracleUtilities.filterSuperTypes( packageName,
imports,
projectSuperTypes ) );
//Filter and rename Type Annotations based on package name and imports
filteredTypeAnnotations = new HashMap<String, Set<Annotation>>();
filteredTypeAnnotations.putAll( AsyncPackageDataModelOracleUtilities.filterTypeAnnotations( packageName,
imports,
projectTypeAnnotations ) );
//Filter and rename Type Field Annotations based on package name and imports
filteredTypeFieldsAnnotations = new HashMap<String, Map<String, Set<Annotation>>>();
filteredTypeFieldsAnnotations.putAll( AsyncPackageDataModelOracleUtilities.filterTypeFieldsAnnotations( packageName,
imports,
projectTypeFieldsAnnotations ) );
//Filter and rename Enum definitions based on package name and imports
filteredEnumLists = new FilteredEnumLists();
filteredEnumLists.putAll( packageWorkbenchEnumLists );
filteredEnumLists.putAll( AsyncPackageDataModelOracleUtilities.filterEnumDefinitions( packageName,
imports,
projectJavaEnumLists ) );
//Filter and rename based on package name and imports
filteredFieldParametersType = new HashMap<String, String>();
filteredFieldParametersType.putAll( AsyncPackageDataModelOracleUtilities.filterFieldParametersTypes( packageName,
imports,
projectFieldParametersType ) );
}
// ####################################
// Population of DMO
// ####################################
@Override
public void setProjectName( final String projectName ) {
this.projectName = projectName;
}
@Override
public void setPackageName( final String packageName ) {
this.packageName = packageName;
}
@Override
public void addModelFields( final Map<String, ModelField[]> modelFields ) {
for ( ModelField[] value : modelFields.values() ) {
if ( value != null ) {
Arrays.sort( value, getModelFieldComparator() );
}
}
this.projectModelFields.putAll( modelFields );
}
@Override
public void addFieldParametersType( final Map<String, String> fieldParametersType ) {
this.projectFieldParametersType.putAll( fieldParametersType );
}
@Override
public void addEventTypes( final Map<String, Boolean> eventTypes ) {
this.projectEventTypes.putAll( eventTypes );
}
@Override
public void addTypeSources( final Map<String, TypeSource> typeSources ) {
this.projectTypeSources.putAll( typeSources );
}
@Override
public void addSuperTypes( final Map<String, List<String>> superTypes ) {
for ( List<String> value : superTypes.values() ) {
if ( value != null ) {
Collections.sort( value, SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
}
}
this.projectSuperTypes.putAll( superTypes );
}
@Override
public void addTypeAnnotations( final Map<String, Set<Annotation>> annotations ) {
this.projectTypeAnnotations.putAll( annotations );
}
@Override
public void addTypeFieldsAnnotations( final Map<String, Map<String, Set<Annotation>>> typeFieldsAnnotations ) {
this.projectTypeFieldsAnnotations.putAll( typeFieldsAnnotations );
}
@Override
public void addJavaEnumDefinitions( final Map<String, String[]> dataEnumLists ) {
this.projectJavaEnumLists.putAll( dataEnumLists );
}
@Override
public void addMethodInformation( final Map<String, List<MethodInfo>> methodInformation ) {
for ( List<MethodInfo> value : methodInformation.values() ) {
if ( value != null ) {
Collections.sort( value, getMethodInfoComparator() );
}
}
this.projectMethodInformation.putAll( methodInformation );
}
@Override
public void addCollectionTypes( final Map<String, Boolean> collectionTypes ) {
this.projectCollectionTypes.putAll( collectionTypes );
}
@Override
public void addPackageNames( final List<String> packageNames ) {
Collections.sort( packageNames, SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
this.packageNames.addAll( packageNames );
}
@Override
public void addWorkbenchEnumDefinitions( final Map<String, String[]> dataEnumLists ) {
this.packageWorkbenchEnumLists.putAll( dataEnumLists );
}
@Override
public void addDslConditionSentences( final List<DSLSentence> dslConditionSentences ) {
this.packageDSLConditionSentences.addAll( dslConditionSentences );
}
@Override
public void addDslActionSentences( final List<DSLSentence> dslActionSentences ) {
this.packageDSLActionSentences.addAll( dslActionSentences );
}
@Override
public void addGlobals( final Map<String, String> packageGlobalTypes ) {
this.packageGlobalTypes.putAll( packageGlobalTypes );
}
private Comparator<ModelField> getModelFieldComparator() {
return new Comparator<ModelField>() {
@Override
public int compare( final ModelField modelField1,
final ModelField modelField2 ) {
return SortHelper.ALPHABETICAL_ORDER_COMPARATOR.compare( modelField1.getName(),
modelField2.getName() );
}
};
}
private Comparator<MethodInfo> getMethodInfoComparator() {
return new Comparator<MethodInfo>() {
@Override
public int compare( final MethodInfo methodInfo1,
final MethodInfo methodInfo2 ) {
int result = SortHelper.ALPHABETICAL_ORDER_COMPARATOR.compare( methodInfo1.getName(),
methodInfo2.getName() );
if ( result == 0 ) {
if ( methodInfo1.getParams() != null && methodInfo2.getParams() == null ) {
return 1;
} else if ( methodInfo1.getParams() == null && methodInfo2.getParams() != null ) {
return -1;
} else if ( methodInfo1.getParams() != null && methodInfo2.getParams() != null ) {
result = methodInfo1.getParams().size() - methodInfo2.getParams().size();
if ( result == 0 ) {
for ( int i = 0; i < methodInfo1.getParams().size() && result == 0; i++ ) {
result = SortHelper.ALPHABETICAL_ORDER_COMPARATOR.compare( methodInfo1.getParams().get( i ),
methodInfo2.getParams().get( i ) );
}
}
}
}
return result;
}
};
}
}