/*
* Copyright 2012 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.services.datamodel.backend.server;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.drools.workbench.models.datamodel.oracle.Annotation;
import org.drools.workbench.models.datamodel.oracle.DataType;
import org.drools.workbench.models.datamodel.oracle.MethodInfo;
import org.drools.workbench.models.datamodel.oracle.ModelField;
import org.drools.workbench.models.datamodel.oracle.PackageDataModelOracle;
import org.drools.workbench.models.datamodel.oracle.ProjectDataModelOracle;
import org.drools.workbench.models.datamodel.oracle.TypeSource;
import org.kie.workbench.common.services.datamodel.util.SortHelper;
import org.kie.workbench.common.services.datamodel.model.LazyModelField;
import org.kie.workbench.common.services.datamodel.model.PackageDataModelOracleBaselinePayload;
import org.kie.workbench.common.services.datamodel.model.PackageDataModelOracleIncrementalPayload;
/**
* Utilities to query ProjectDMO content
*/
public class DataModelOracleUtilities {
/**
* Convenience method to get an array of all fully qualified class names available in a project
* @param oracle The DMO representing a project
* @return
*/
public static String[] getFactTypes( final ProjectDataModelOracle oracle ) {
List<String> packageNames = oracle.getProjectPackageNames();
final Map<String, ModelField[]> modelFields = oracle.getProjectModelFields();
final List<String> types = new ArrayList<String>();
for ( String type : modelFields.keySet() ) {
int beginIndex = type.lastIndexOf( '.' );
if ( beginIndex < 0 ) {
types.add( type );
} else {
String substring = type.substring( 0, beginIndex );
if ( packageNames.contains( substring ) ) {
types.add( type );
}
}
}
Collections.sort( types, SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
return types.toArray( new String[ types.size() ] );
}
/**
* Convenience method to get an array of field names for a type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @return
*/
public static String[] getFieldNames( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName ) {
final ModelField[] modelFields = oracle.getProjectModelFields().get( fullyQualifiedClassName );
if ( modelFields == null ) {
return new String[ 0 ];
}
final String[] fieldNames = new String[ modelFields.length ];
for ( int i = 0; i < modelFields.length; i++ ) {
fieldNames[ i ] = modelFields[ i ].getName();
}
Arrays.sort( fieldNames, SortHelper.ALPHABETICAL_ORDER_COMPARATOR );
return fieldNames;
}
/**
* Convenience method to get the fully qualified class name of the super type of another type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @return
*/
public static String getSuperType( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName ) {
List<String> superTypes = oracle.getProjectSuperTypes().get( fullyQualifiedClassName );
if ( superTypes != null && superTypes.size() > 0 ) {
return superTypes.get( 0 );
} else {
return null;
}
}
/**
* Convenience method to get a set of annotations on a type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @return
*/
public static Set<Annotation> getTypeAnnotations( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName ) {
final Map<String, Set<Annotation>> typeAnnotations = oracle.getProjectTypeAnnotations();
if ( !typeAnnotations.containsKey( fullyQualifiedClassName ) ) {
return Collections.EMPTY_SET;
}
return typeAnnotations.get( fullyQualifiedClassName );
}
/**
* Convenience method to get all field annotations on a type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @return
*/
public static Map<String, Set<Annotation>> getTypeFieldsAnnotations( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName ) {
final Map<String, Map<String, Set<Annotation>>> typeFieldsAnnotations = oracle.getProjectTypeFieldsAnnotations();
if ( !typeFieldsAnnotations.containsKey( fullyQualifiedClassName ) ) {
return Collections.EMPTY_MAP;
}
return typeFieldsAnnotations.get( fullyQualifiedClassName );
}
/**
* Convenience method to get the fully qualified class name of a field on a type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @param fieldName The field Name
* @return
*/
public static String getFieldClassName( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName,
final String fieldName ) {
final ModelField field = getField( oracle,
fullyQualifiedClassName,
fieldName );
return field == null ? null : field.getClassName();
}
private static ModelField getField( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName,
final String fieldName ) {
final String shortName = getFactNameFromType( oracle,
fullyQualifiedClassName );
final ModelField[] fields = oracle.getProjectModelFields().get( shortName );
if ( fields == null ) {
return null;
}
for ( ModelField modelField : fields ) {
if ( modelField.getName().equals( fieldName ) ) {
return modelField;
}
}
return null;
}
private static String getFactNameFromType( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName ) {
if ( fullyQualifiedClassName == null ) {
return null;
}
if ( oracle.getProjectModelFields().containsKey( fullyQualifiedClassName ) ) {
return fullyQualifiedClassName;
}
for ( Map.Entry<String, ModelField[]> entry : oracle.getProjectModelFields().entrySet() ) {
for ( ModelField mf : entry.getValue() ) {
if ( DataType.TYPE_THIS.equals( mf.getName() ) && fullyQualifiedClassName.equals( mf.getClassName() ) ) {
return entry.getKey();
}
}
}
return null;
}
/**
* Convenience method to get the generic type of a field on a type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @param fieldName The field Name
* @return
*/
public static String getParametricFieldType( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName,
final String fieldName ) {
final String qualifiedFactFieldName = fullyQualifiedClassName + "#" + fieldName;
return oracle.getProjectFieldParametersType().get( qualifiedFactFieldName );
}
/**
* Convenience method to get the source of a type in a project
* @param oracle The DMO representing a project
* @param fullyQualifiedClassName The FQCN of the type
* @return
*/
public static TypeSource getTypeSource( final ProjectDataModelOracle oracle,
final String fullyQualifiedClassName ) {
return oracle.getProjectTypeSources().get( fullyQualifiedClassName );
}
public static void populateDataModel( final PackageDataModelOracle oracle,
final PackageDataModelOracleBaselinePayload dataModel,
final Set<String> usedFullyQualifiedClassNames ) {
dataModel.setProjectName( oracle.getProjectName() );
dataModel.setPackageName( oracle.getPackageName() );
dataModel.setModelFields( setupModelFields( usedFullyQualifiedClassNames,
oracle.getProjectModelFields(),
oracle.getPackageGlobals() ) );
dataModel.setFieldParametersType( filterFieldParametersTypes( usedFullyQualifiedClassNames,
oracle.getProjectFieldParametersType() ) );
dataModel.setEventTypes( filterEventTypes( usedFullyQualifiedClassNames,
oracle.getProjectEventTypes() ) );
dataModel.setTypeSources( filterTypeSources( usedFullyQualifiedClassNames,
oracle.getProjectTypeSources() ) );
dataModel.setSuperTypes( filterSuperTypes( usedFullyQualifiedClassNames,
oracle.getProjectSuperTypes() ) );
dataModel.setTypeAnnotations( filterTypeAnnotations( usedFullyQualifiedClassNames,
oracle.getProjectTypeAnnotations() ) );
dataModel.setTypeFieldsAnnotations( filterTypeFieldsAnnotations( usedFullyQualifiedClassNames,
oracle.getProjectTypeFieldsAnnotations() ) );
dataModel.setJavaEnumDefinitions( oracle.getProjectJavaEnumDefinitions() );
dataModel.setWorkbenchEnumDefinitions( oracle.getPackageWorkbenchDefinitions() );
dataModel.setMethodInformation( filterMethodInformation( usedFullyQualifiedClassNames,
oracle.getProjectMethodInformation() ) );
dataModel.setCollectionTypes( filterCollectionTypes( usedFullyQualifiedClassNames,
oracle.getProjectCollectionTypes() ) );
dataModel.setDslConditionSentences( oracle.getPackageDslConditionSentences() );
dataModel.setDslActionSentences( oracle.getPackageDslActionSentences() );
dataModel.setGlobalTypes( oracle.getPackageGlobals() );
dataModel.setPackageNames( oracle.getProjectPackageNames() );
}
public static void populateDataModel( final PackageDataModelOracle oracle,
final PackageDataModelOracleIncrementalPayload dataModel,
final String usedFullyQualifiedClassName ) {
final Set<String> usedFullyQualifiedClassNames = new HashSet<String>();
usedFullyQualifiedClassNames.add( usedFullyQualifiedClassName );
dataModel.setModelFields( filterModelFields( usedFullyQualifiedClassNames,
oracle.getProjectModelFields() ) );
dataModel.setFieldParametersType( filterFieldParametersTypes( usedFullyQualifiedClassNames,
oracle.getProjectFieldParametersType() ) );
dataModel.setEventTypes( filterEventTypes( usedFullyQualifiedClassNames,
oracle.getProjectEventTypes() ) );
dataModel.setTypeSources( filterTypeSources( usedFullyQualifiedClassNames,
oracle.getProjectTypeSources() ) );
dataModel.setSuperTypes( filterSuperTypes( usedFullyQualifiedClassNames,
oracle.getProjectSuperTypes() ) );
dataModel.setTypeAnnotations( filterTypeAnnotations( usedFullyQualifiedClassNames,
oracle.getProjectTypeAnnotations() ) );
dataModel.setTypeFieldsAnnotations( filterTypeFieldsAnnotations( usedFullyQualifiedClassNames,
oracle.getProjectTypeFieldsAnnotations() ) );
dataModel.setMethodInformation( filterMethodInformation( usedFullyQualifiedClassNames,
oracle.getProjectMethodInformation() ) );
dataModel.setCollectionTypes( filterCollectionTypes( usedFullyQualifiedClassNames,
oracle.getProjectCollectionTypes() ) );
}
//Setup Model Fields for lazy loading client-side
private static Map<String, ModelField[]> setupModelFields( final Set<String> usedFullyQualifiedClassNames,
final Map<String, ModelField[]> projectModelFields,
final Map<String, String> packageGlobals ) {
final Map<String, ModelField[]> scopedModelFields = new HashMap<String, ModelField[]>();
for ( Map.Entry<String, ModelField[]> e : projectModelFields.entrySet() ) {
final String mfQualifiedType = e.getKey();
if ( usedFullyQualifiedClassNames.contains( mfQualifiedType ) ) {
scopedModelFields.put( mfQualifiedType,
e.getValue() );
} else if ( packageGlobals.containsValue( mfQualifiedType ) ) {
scopedModelFields.put( mfQualifiedType,
e.getValue() );
} else {
scopedModelFields.put( mfQualifiedType,
makeLazyProxyModelField( e.getValue() ) );
}
}
return scopedModelFields;
}
//AsyncPackageDataModelOracle.getFactNameFromType() uses THIS to determine the simple Type from a FQCN.
//Therefore ensure we provide this minimal information for every Type in the DMO to prevent getFactNameFromType()
//needing a callback to the server which makes things more complicated than really needed.
private static ModelField[] makeLazyProxyModelField( final ModelField[] modelFields ) {
for ( ModelField modelField : modelFields ) {
if ( DataType.TYPE_THIS.equals( modelField.getName() ) ) {
final ModelField[] result = new ModelField[ 1 ];
//LazyModelField is a place-holder to tell AsyncPackageDataModelOracle that it needs to load more information
result[ 0 ] = new LazyModelField( modelField.getName(),
modelField.getClassName(),
modelField.getClassType(),
modelField.getOrigin(),
modelField.getAccessorsAndMutators(),
modelField.getType() );
return result;
}
}
return null;
}
//Filter Model Fields by the types used
private static Map<String, ModelField[]> filterModelFields( final Set<String> usedFullyQualifiedClassNames,
final Map<String, ModelField[]> projectModelFields ) {
final Map<String, ModelField[]> scopedModelFields = new HashMap<String, ModelField[]>();
for ( Map.Entry<String, ModelField[]> e : projectModelFields.entrySet() ) {
final String mfQualifiedType = e.getKey();
final ModelField[] mfModelFields = e.getValue();
if ( isTypeUsed( mfQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedModelFields.put( mfQualifiedType,
mfModelFields );
}
}
return scopedModelFields;
}
//Filter Collection Types by the types used
private static Map<String, Boolean> filterCollectionTypes( final Set<String> usedFullyQualifiedClassNames,
final Map<String, Boolean> projectCollectionTypes ) {
final Map<String, Boolean> scopedCollectionTypes = new HashMap<String, Boolean>();
for ( Map.Entry<String, Boolean> e : projectCollectionTypes.entrySet() ) {
final String collectionQualifiedType = e.getKey();
if ( isTypeUsed( collectionQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedCollectionTypes.put( collectionQualifiedType,
e.getValue() );
}
}
return scopedCollectionTypes;
}
//Filter Event Types by the types used
private static Map<String, Boolean> filterEventTypes( final Set<String> usedFullyQualifiedClassNames,
final Map<String, Boolean> projectEventTypes ) {
final Map<String, Boolean> scopedEventTypes = new HashMap<String, Boolean>();
for ( Map.Entry<String, Boolean> e : projectEventTypes.entrySet() ) {
final String eventQualifiedType = e.getKey();
if ( isTypeUsed( eventQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedEventTypes.put( eventQualifiedType,
e.getValue() );
}
}
return scopedEventTypes;
}
//Filter TypeSource by the types used
private static Map<String, TypeSource> filterTypeSources( final Set<String> usedFullyQualifiedClassNames,
final Map<String, TypeSource> projectTypeSources ) {
final Map<String, TypeSource> scopedTypeSources = new HashMap<String, TypeSource>();
for ( Map.Entry<String, TypeSource> e : projectTypeSources.entrySet() ) {
final String typeQualifiedType = e.getKey();
if ( isTypeUsed( typeQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedTypeSources.put( typeQualifiedType,
e.getValue() );
}
}
return scopedTypeSources;
}
//Filter Super Types by the types used
private static Map<String, List<String>> filterSuperTypes( final Set<String> usedFullyQualifiedClassNames,
final Map<String, List<String>> projectSuperTypes ) {
final Map<String, List<String>> scopedSuperTypes = new HashMap<String, List<String>>();
for ( Map.Entry<String, List<String>> e : projectSuperTypes.entrySet() ) {
final String typeQualifiedType = e.getKey();
final List<String> superTypeQualifiedTypes = e.getValue();
if ( isTypeUsed( typeQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedSuperTypes.put( typeQualifiedType,
superTypeQualifiedTypes );
}
}
return scopedSuperTypes;
}
//Filter Type Annotations by the types used
private static Map<String, Set<Annotation>> filterTypeAnnotations( final Set<String> usedFullyQualifiedClassNames,
final Map<String, Set<Annotation>> projectTypeAnnotations ) {
final Map<String, Set<Annotation>> scopedTypeAnnotations = new HashMap<String, Set<Annotation>>();
for ( Map.Entry<String, Set<Annotation>> e : projectTypeAnnotations.entrySet() ) {
final String typeAnnotationQualifiedType = e.getKey();
if ( isTypeUsed( typeAnnotationQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedTypeAnnotations.put( typeAnnotationQualifiedType,
e.getValue() );
}
}
return scopedTypeAnnotations;
}
//Filter Type Fields Annotations by the types used
private static Map<String, Map<String, Set<Annotation>>> filterTypeFieldsAnnotations( final Set<String> usedFullyQualifiedClassNames,
final Map<String, Map<String, Set<Annotation>>> projectTypeFieldsAnnotations ) {
final Map<String, Map<String, Set<Annotation>>> scopedTypeFieldsAnnotations = new HashMap<String, Map<String, Set<Annotation>>>();
for ( Map.Entry<String, Map<String, Set<Annotation>>> e : projectTypeFieldsAnnotations.entrySet() ) {
final String typeAnnotationQualifiedType = e.getKey();
if ( isTypeUsed( typeAnnotationQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedTypeFieldsAnnotations.put( typeAnnotationQualifiedType,
e.getValue() );
}
}
return scopedTypeFieldsAnnotations;
}
//Filter Method Information (used by ActionCallXXX and ExpressionBuilder) by the types used
private static Map<String, List<MethodInfo>> filterMethodInformation( final Set<String> usedFullyQualifiedClassNames,
final Map<String, List<MethodInfo>> projectMethodInformation ) {
final Map<String, List<MethodInfo>> scopedMethodInformation = new HashMap<String, List<MethodInfo>>();
for ( Map.Entry<String, List<MethodInfo>> e : projectMethodInformation.entrySet() ) {
final String miQualifiedType = e.getKey();
if ( isTypeUsed( miQualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedMethodInformation.put( miQualifiedType,
e.getValue() );
}
}
return scopedMethodInformation;
}
//Filter Field Parameter Types by the types used
private static Map<String, String> filterFieldParametersTypes( final Set<String> usedFullyQualifiedClassNames,
final Map<String, String> projectFieldParametersTypes ) {
final Map<String, String> scopedFieldParametersType = new HashMap<String, String>();
for ( Map.Entry<String, String> e : projectFieldParametersTypes.entrySet() ) {
final String fieldName = e.getKey();
final String fieldType = e.getValue();
final String fFieldName_QualifiedType = getQualifiedTypeFromEncodedFieldName( fieldName );
if ( isTypeUsed( fFieldName_QualifiedType,
usedFullyQualifiedClassNames ) ) {
scopedFieldParametersType.put( fieldName,
fieldType );
}
}
return scopedFieldParametersType;
}
private static String getQualifiedTypeFromEncodedFieldName( final String encodedFieldName ) {
String typeName = encodedFieldName;
int hashIndex = typeName.lastIndexOf( "#" );
if ( hashIndex != -1 ) {
typeName = typeName.substring( 0,
hashIndex );
}
return typeName;
}
private static boolean isTypeUsed( final String fullyQualifiedClassName,
final Set<String> usedFullyQualifiedClassNames ) {
return usedFullyQualifiedClassNames.contains( fullyQualifiedClassName );
}
}