2017-06-02 00:39:03 -04:00
package com.moparisthebest.jdbc.codegen ;
import javax.lang.model.element.* ;
2020-10-20 22:58:46 -04:00
import javax.lang.model.type.DeclaredType ;
import javax.lang.model.type.TypeKind ;
2017-06-02 00:39:03 -04:00
import javax.lang.model.type.TypeMirror ;
2020-10-20 22:58:46 -04:00
import javax.tools.Diagnostic ;
import java.util.* ;
import static com.moparisthebest.jdbc.codegen.CompileTimeRowToObjectMapper.getAllImplementedTypes ;
import static com.moparisthebest.jdbc.codegen.JdbcMapperProcessor.booleanType ;
import static com.moparisthebest.jdbc.codegen.JdbcMapperProcessor.types ;
2017-06-02 00:39:03 -04:00
/ * *
* Created by mopar on 6 / 1 / 17 .
* /
2020-10-20 22:58:46 -04:00
class SpecialVariableElement extends DelegatingVariableElement {
2018-03-14 02:25:11 -04:00
enum SpecialType {
2018-05-22 00:08:14 -04:00
BIND_IN_LIST ,
2018-03-14 02:25:11 -04:00
IN_LIST ,
CLOB ,
BLOB ,
2019-02-07 01:09:15 -05:00
SQL ,
2020-06-17 01:36:10 -04:00
STR_BOOLEAN ,
2020-10-20 22:58:46 -04:00
PLAIN ,
2018-03-14 02:25:11 -04:00
}
2017-06-02 00:39:03 -04:00
2018-03-14 02:25:11 -04:00
final SpecialType specialType ;
final String blobStringCharset ;
2018-05-12 00:30:57 -04:00
final int index ;
2020-10-20 22:58:46 -04:00
final boolean iterable , bindable , allowReflection ;
TypeMirror type ;
2018-03-14 02:25:11 -04:00
2018-05-22 00:08:14 -04:00
String name , componentTypeString ;
2020-10-20 22:58:46 -04:00
SpecialVariableElement ( final boolean allowReflection , final VariableElement delegate , final String paramName , final int indexOfFirstPeriod , final SpecialType specialType ) {
this ( allowReflection , delegate , paramName , indexOfFirstPeriod , specialType , null , 0 ) ;
2018-05-12 00:30:57 -04:00
}
2020-10-20 22:58:46 -04:00
SpecialVariableElement ( final boolean allowReflection , final VariableElement delegate , final String paramName , final int indexOfFirstPeriod , final SpecialType specialType , final int index ) {
this ( allowReflection , delegate , paramName , indexOfFirstPeriod , specialType , null , index ) ;
2018-03-14 02:25:11 -04:00
}
2017-06-02 00:39:03 -04:00
2020-10-20 22:58:46 -04:00
SpecialVariableElement ( final boolean allowReflection , final VariableElement delegate , final String paramName , final int indexOfFirstPeriod , final SpecialType specialType , final String blobStringCharset ) {
this ( allowReflection , delegate , paramName , indexOfFirstPeriod , specialType , blobStringCharset , 0 ) ;
2018-05-12 00:30:57 -04:00
}
2020-10-20 22:58:46 -04:00
SpecialVariableElement ( final boolean allowReflection , final VariableElement delegate , final String paramName , final int indexOfPeriod , final SpecialType specialType , final String blobStringCharset , final int index ) {
super ( delegate ) ;
this . allowReflection = allowReflection ;
2018-03-14 02:25:11 -04:00
this . specialType = specialType ;
this . blobStringCharset = blobStringCharset ;
2018-05-12 00:30:57 -04:00
this . index = index ;
2020-10-20 22:58:46 -04:00
if ( indexOfPeriod = = - 1 ) {
// no recursion or anything complicated, straight parameter
this . name = paramName ;
this . type = delegate . asType ( ) ;
} else {
final String [ ] paramSplit = paramName . split ( " \\ s* \\ . \\ s* " ) ;
if ( paramSplit . length < 2 ) {
JdbcMapperProcessor . messager . printMessage ( Diagnostic . Kind . ERROR , " paramName invalid with period at end: " + paramName , delegate ) ;
throw new RuntimeException ( " paramName invalid with period at end: " + paramName ) ;
}
final StringBuilder sb = new StringBuilder ( ) ;
appendVar ( paramSplit [ 0 ] , sb ) ;
this . type = delegate . asType ( ) ;
for ( int x = 1 ; x < paramSplit . length ; + + x ) {
appendVar ( paramSplit [ x ] , sb ) ;
}
this . name = sb . toString ( ) ;
}
this . iterable = specialType = = SpecialType . SQL & & types . isAssignable ( delegate . asType ( ) , JdbcMapperProcessor . iterableType ) ;
this . bindable = ! this . iterable & & specialType = = SpecialType . SQL & & types . isAssignable ( delegate . asType ( ) , JdbcMapperProcessor . bindableType ) ;
}
public void appendVar ( final String fullParam , final StringBuilder sb ) {
final boolean nullSafe = fullParam . endsWith ( " ? " ) ;
final String param = nullSafe ? fullParam . substring ( 0 , fullParam . length ( ) - 1 ) : fullParam ;
// hack for first param
if ( type ! = null ) {
final String name = getGetterTypeAppendString ( param , sb ) ;
if ( name = = null ) {
JdbcMapperProcessor . messager . printMessage ( Diagnostic . Kind . ERROR , " recursive param not found: " + param , delegate ) ;
}
sb . append ( name ) ;
} else {
sb . append ( param ) ;
}
if ( nullSafe ) {
final String var = sb . toString ( ) ;
sb . setLength ( 0 ) ;
sb . append ( " (( " ) . append ( var ) . append ( " ) == null ? null : ( " ) . append ( var ) . append ( " )) " ) ;
}
}
public String getGetterTypeAppendString ( final String fieldName , final StringBuilder sb ) {
if ( type . getKind ( ) ! = TypeKind . DECLARED ) {
JdbcMapperProcessor . messager . printMessage ( Diagnostic . Kind . ERROR , " type " + type + " not TypeKind.DECLARED ?? how?? fieldName: " + fieldName , delegate ) ;
return " " ;
}
final DeclaredType declaredReturnType = ( DeclaredType ) type ;
final List < DeclaredType > allTypes = getAllImplementedTypes ( declaredReturnType , new ArrayList < DeclaredType > ( ) ) ;
// public methods
// have to loop to get super methods too
final String methodSuffix = Character . toUpperCase ( fieldName . charAt ( 0 ) ) + fieldName . substring ( 1 ) ;
final String getMethodName = " get " + methodSuffix , isMethodName = " is " + methodSuffix ;
for ( final DeclaredType clazz : allTypes ) {
for ( Element e : ( ( TypeElement ) clazz . asElement ( ) ) . getEnclosedElements ( ) ) {
if ( e . getKind ( ) ! = ElementKind . METHOD )
continue ;
final ExecutableElement m = ( ExecutableElement ) e ;
//System.out.printf("method: '%s', isSetterMethod: '%s'\n", m, isSetterMethod(m));
final String ret = matchingGetterMethod ( m , getMethodName , isMethodName ) ;
if ( ret ! = null ) {
return ret ;
}
}
}
// fix for 8813: include inherited and non-public fields
for ( final DeclaredType clazz : allTypes ) {
//System.out.println("fields in class: "+Arrays.toString(classFields));
for ( Element e : ( ( TypeElement ) clazz . asElement ( ) ) . getEnclosedElements ( ) ) {
if ( e . getKind ( ) ! = ElementKind . FIELD )
continue ;
final VariableElement f = ( VariableElement ) e ;
final Set < Modifier > modifiers = f . getModifiers ( ) ;
// we want the name to match exactly
if ( ! fieldName . equals ( f . getSimpleName ( ) . toString ( ) ) ) continue ;
// cannot be static
if ( modifiers . contains ( Modifier . STATIC ) ) return null ;
// must be public todo: what about package-private?
if ( modifiers . contains ( Modifier . PUBLIC ) ) {
this . type = f . asType ( ) ;
return " . " + fieldName ;
}
if ( ! allowReflection ) {
JdbcMapperProcessor . messager . printMessage ( Diagnostic . Kind . ERROR , " cannot public setter, but did find non-public field on parameter: ' " + delegate . getSimpleName ( ) + " ' with this name: ' " + fieldName + " ', but reflection is not allowed " , delegate ) ;
return " " ;
}
// otherwise support terrible reflection
final TypeMirror oldType = this . type ;
this . type = f . asType ( ) ;
final String obj = sb . toString ( ) ;
sb . setLength ( 0 ) ;
return " com.moparisthebest.jdbc.util.ReflectionUtil.getValue( " + oldType . toString ( ) + " .class, \" " + fieldName + " \" , " +
this . type . toString ( ) + " .class, ( " + obj + " )) " ;
}
}
JdbcMapperProcessor . messager . printMessage ( Diagnostic . Kind . ERROR , " cannot find field or public setter on parameter: ' " + delegate . getSimpleName ( ) + " ' with this name: ' " + fieldName + " ' " , delegate ) ;
return " " ;
}
/ * *
* Determine if the given method is a java bean setter method .
* @param method Method to check
* @param getMethodName
* @param isMethodName
* @return True if the method is a setter method .
* /
public String matchingGetterMethod ( final ExecutableElement method , final String getMethodName , final String isMethodName ) {
final String methodName = method . getSimpleName ( ) . toString ( ) ;
final boolean isMethod = isMethodName . equals ( methodName ) ;
if ( isMethod | | getMethodName . equals ( methodName ) ) {
final Set < Modifier > modifiers = method . getModifiers ( ) ;
// cannot be static
if ( modifiers . contains ( Modifier . STATIC ) ) return null ;
// must be public todo: what about package-private?
if ( ! modifiers . contains ( Modifier . PUBLIC ) ) return null ;
// must take no parameters
if ( method . getParameters ( ) . size ( ) ! = 0 ) return null ;
final TypeMirror ret = method . getReturnType ( ) ;
// must return boolean/Boolean to qualify for is*
if ( isMethod & & ! ( ret . getKind ( ) = = TypeKind . BOOLEAN | | types . isAssignable ( ret , booleanType ) ) ) return null ;
this . type = ret ;
return " . " + methodName + " () " ;
}
return null ;
2018-05-22 00:08:14 -04:00
}
public String getName ( ) {
return name ;
}
public void setName ( String name ) {
this . name = name ;
}
public String getComponentTypeString ( ) {
return componentTypeString ;
}
public void setComponentTypeString ( String componentTypeString ) {
this . componentTypeString = componentTypeString ;
2017-06-02 00:39:03 -04:00
}
@Override
public TypeMirror asType ( ) {
2020-10-20 22:58:46 -04:00
return type ;
2017-06-02 00:39:03 -04:00
}
2018-03-14 02:25:11 -04:00
@Override
public String toString ( ) {
2019-03-12 00:34:23 -04:00
return " SpecialVariableElement{ " +
2018-03-14 02:25:11 -04:00
" delegate= " + delegate +
'}' ;
}
2017-06-02 00:39:03 -04:00
}