/* * Copyright 2015 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. * * 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.workbench.models.commons.backend.rule; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.drools.core.util.DateUtils; import org.drools.workbench.models.datamodel.imports.Import; import org.drools.workbench.models.datamodel.imports.Imports; 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.rule.ActionFieldList; import org.drools.workbench.models.datamodel.rule.ActionInsertFact; import org.drools.workbench.models.datamodel.rule.ActionSetField; import org.drools.workbench.models.datamodel.rule.FieldNatureType; import org.drools.workbench.models.datamodel.rule.RuleModel; import java.math.BigDecimal; import java.math.BigInteger; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; class RuleModelPersistenceHelper { static String unwrapParenthesis( final String s ) { int start = s.indexOf( '(' ); int end = s.lastIndexOf( ')' ); if ( start < 0 || end < 0 ) { return s; } return s.substring( start + 1, end ).trim(); } static String unwrapTemplateKey( final String s ) { int start = s.indexOf( "@{" ); if ( start < 0 ) { return s; } int end = s.indexOf( "}", start ); if ( end < 0 ) { return s; } return s.substring( start + 2, end ).trim(); } static String getSimpleFactType( final String className, final PackageDataModelOracle dmo ) { for ( String type : dmo.getProjectModelFields().keySet() ) { if ( type.equals( className ) ) { return type.substring( type.lastIndexOf( "." ) + 1 ); } } return className; } static int inferFieldNature( final String dataType, final String value, final Map<String, String> boundParams, final boolean isJavaDialect ) { if ( boundParams.containsKey( value ) ) { return FieldNatureType.TYPE_VARIABLE; } if ( value.contains( "@{" ) ) { return FieldNatureType.TYPE_TEMPLATE; } return inferFieldNature( dataType, value, isJavaDialect ); } static int inferFieldNature( final String dataType, final String value, final boolean isJavaDialect ) { int nature = ( StringUtils.isEmpty( value ) ? FieldNatureType.TYPE_UNDEFINED : FieldNatureType.TYPE_LITERAL ); if ( dataType == DataType.TYPE_COLLECTION ) { return FieldNatureType.TYPE_FORMULA; } else if ( DataType.TYPE_BOOLEAN.equals( dataType ) ) { if ( !( Boolean.TRUE.equals( Boolean.parseBoolean( value ) ) || Boolean.FALSE.equals( Boolean.parseBoolean( value ) ) ) ) { return FieldNatureType.TYPE_FORMULA; } else { return FieldNatureType.TYPE_LITERAL; } } else if ( DataType.TYPE_DATE.equals( dataType ) ) { try { new SimpleDateFormat( DateUtils.getDateFormatMask(), Locale.ENGLISH ).parse( adjustParam( dataType, value, Collections.EMPTY_MAP, isJavaDialect ) ); return FieldNatureType.TYPE_LITERAL; } catch ( ParseException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_STRING.equals( dataType ) ) { if ( isStringLiteral( value ) ) { return FieldNatureType.TYPE_LITERAL; } else { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC.equals( dataType ) ) { if ( !NumberUtils.isNumber( value ) ) { return FieldNatureType.TYPE_FORMULA; } return FieldNatureType.TYPE_LITERAL; } else if ( DataType.TYPE_NUMERIC_BIGDECIMAL.equals( dataType ) ) { try { new BigDecimal( adjustParam( dataType, value, Collections.EMPTY_MAP, isJavaDialect ) ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_BIGINTEGER.equals( dataType ) ) { try { new BigInteger( adjustParam( dataType, value, Collections.EMPTY_MAP, isJavaDialect ) ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_BYTE.equals( dataType ) ) { try { new Byte( value ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_DOUBLE.equals( dataType ) ) { try { new Double( value ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_FLOAT.equals( dataType ) ) { try { new Float( value ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_INTEGER.equals( dataType ) ) { try { new Integer( value ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_LONG.equals( dataType ) ) { try { new Long( value ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } else if ( DataType.TYPE_NUMERIC_SHORT.equals( dataType ) ) { try { new Short( value ); return FieldNatureType.TYPE_LITERAL; } catch ( NumberFormatException e ) { return FieldNatureType.TYPE_FORMULA; } } return nature; } private static boolean isStringLiteral( final String value ) { boolean escape = false; boolean inString = false; for ( char c : value.toCharArray() ) { if ( escape ) { escape = false; continue; } if ( !inString ) { if ( !( c == ' ' || c == '\t' || c == '"' ) ) { return false; } } if ( c == '"' ) { inString = !inString; } else { escape = c == '\\'; } } return true; } static ModelField[] findFields( final RuleModel m, final PackageDataModelOracle dmo, final String type ) { ModelField[] fields = dmo.getProjectModelFields().get( type ); if ( fields != null ) { return fields; } for ( String i : m.getImports().getImportStrings() ) { if ( i.endsWith( "." + type ) ) { fields = dmo.getProjectModelFields().get( i ); if ( fields != null ) { return fields; } } } return dmo.getProjectModelFields().get( m.getPackageName() + "." + type ); } static ModelField findField( final ModelField[] typeFields, final String fieldName ) { if ( typeFields != null && fieldName != null ) { for ( ModelField typeField : typeFields ) { if ( typeField.getName().equals( fieldName ) ) { return typeField; } } } return null; } static MethodInfo findMethodInfo( final List<MethodInfo> methodInfos, final String expressionPart ) { if ( methodInfos != null && expressionPart != null ) { //Find a MethodInfo that matches name and parameter count final int expressionParameterCount = parseExpressionParameters( expressionPart ).size(); final String normalizedExpressionPart = normalizeExpressionPart( expressionPart ); for ( MethodInfo methodInfo : methodInfos ) { if ( methodInfo.getName().equals( normalizedExpressionPart ) && methodInfo.getParams().size() == expressionParameterCount ) { return methodInfo; } } } return null; } //TODO This is a naive implementation that won't handle parameter values containing "," //TODO for example callMyMethod("Anstis, Michael", 41). This would parse as 3 parameters static List<String> parseExpressionParameters( String expressionPart ) { int parenthesisOpenPos = expressionPart.indexOf( '(' ); int parenthesisClosePos = expressionPart.lastIndexOf( ')' ); if ( parenthesisOpenPos > 0 && parenthesisClosePos > 0 ) { expressionPart = expressionPart.substring( parenthesisOpenPos + 1, parenthesisClosePos ); } if ( expressionPart.isEmpty() ) { return Collections.emptyList(); } return Arrays.asList( expressionPart.split( "," ) ); } private static String normalizeExpressionPart( String expressionPart ) { int parenthesisPos = expressionPart.indexOf( '(' ); if ( parenthesisPos > 0 ) { expressionPart = expressionPart.substring( 0, parenthesisPos ); } return expressionPart.trim(); } static String inferDataType( final ActionFieldList action, final String field, final Map<String, String> boundParams, final PackageDataModelOracle dmo, final Imports imports ) { String factType = null; if ( action instanceof ActionInsertFact ) { factType = ( (ActionInsertFact) action ).getFactType(); } else if ( action instanceof ActionSetField ) { String boundParam = ( (ActionSetField) action ).getVariable(); factType = boundParams.get( boundParam ); } if ( factType == null ) { return null; } //Lookup without package prefix or imports ModelField[] modelFields = dmo.getProjectModelFields().get( factType ); //Lookup with package prefix if ( modelFields == null ) { String fqcn = dmo.getPackageName() + "." + factType; modelFields = dmo.getProjectModelFields().get( fqcn ); } //Lookup from imports if ( modelFields == null ) { for ( Import item : imports.getImports() ) { if ( item.getType().endsWith( factType ) ) { modelFields = dmo.getProjectModelFields().get( item.getType() ); if ( modelFields != null ) { break; } } } } if ( modelFields == null ) { return null; } for ( ModelField modelField : modelFields ) { if ( modelField.getName().equals( field ) ) { return getSimpleFactType( modelField.getType(), dmo ); } } return null; } static String inferDataType( final String param, final Map<String, String> boundParams, final boolean isJavaDialect ) { if ( param.startsWith( "sdf.parse(\"" ) ) { return DataType.TYPE_DATE; } else if ( param.startsWith( "\"" ) ) { return DataType.TYPE_STRING; } else if ( param.equals( "true" ) || param.equals( "false" ) ) { return DataType.TYPE_BOOLEAN; } else if ( param.endsWith( "B" ) || ( isJavaDialect && param.startsWith( "new java.math.BigDecimal" ) ) ) { return DataType.TYPE_NUMERIC_BIGDECIMAL; } else if ( param.endsWith( "I" ) || ( isJavaDialect && param.startsWith( "new java.math.BigInteger" ) ) ) { return DataType.TYPE_NUMERIC_BIGINTEGER; } else if ( param.startsWith( "[" ) && param.endsWith( "]" ) ) { return DataType.TYPE_COLLECTION; } else if ( boundParams.containsKey( param ) ) { return DataType.TYPE_OBJECT; } return DataType.TYPE_NUMERIC; } static String adjustParam( final String dataType, final String param, final Map<String, String> boundParams, final boolean isJavaDialect ) { if ( DataType.TYPE_DATE.equals( dataType ) ) { if ( param.contains( "sdf.parse(\"" ) ) { return param.substring( "sdf.parse(\"".length(), param.length() - 2 ); } else { return param; } } else if ( DataType.TYPE_STRING.equals( dataType ) ) { if ( param.startsWith( "\"" ) && param.endsWith( "\"" ) ) { return param.substring( 1, param.length() - 1 ); } else { return param; } } else if ( DataType.TYPE_NUMERIC_BIGDECIMAL.equals( dataType ) ) { if ( isJavaDialect ) { return param.substring( "new java.math.BigDecimal(\"".length(), param.length() - 2 ); } else { return param.substring( 0, param.length() - 1 ); } } else if ( DataType.TYPE_NUMERIC_BIGINTEGER.equals( dataType ) ) { if ( isJavaDialect ) { return param.substring( "new java.math.BigInteger(\"".length(), param.length() - 2 ); } else { return param.substring( 0, param.length() - 1 ); } } else if ( boundParams.containsKey( param ) ) { return "=" + param; } return param; } static List<MethodInfo> getMethodInfosForType( final RuleModel model, final PackageDataModelOracle dmo, final String variableType ) { List<MethodInfo> methods = dmo.getProjectMethodInformation().get( variableType ); if ( methods == null ) { for ( String imp : model.getImports().getImportStrings() ) { if ( imp.endsWith( "." + variableType ) ) { methods = dmo.getProjectMethodInformation().get( imp ); if ( methods != null ) { break; } } } } return methods; } static String removeNumericSuffix( final String value, final String dataType ) { if ( DataType.TYPE_NUMERIC_DOUBLE.equals( dataType ) ) { if ( value.endsWith( "d" ) ) { return value.substring( 0, value.indexOf( "d" ) ); } } else if ( DataType.TYPE_NUMERIC_FLOAT.equals( dataType ) ) { if ( value.endsWith( "f" ) ) { return value.substring( 0, value.indexOf( "f" ) ); } } else if ( DataType.TYPE_NUMERIC_LONG.equals( dataType ) ) { if ( value.endsWith( "L" ) ) { return value.substring( 0, value.indexOf( "L" ) ); } } return value; } }