Skip to content

Commit

Permalink
[BACKLOG-7120] Add List support to MDI core
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderBuloichik committed Mar 17, 2016
1 parent 4df9598 commit 45ed84c
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 72 deletions.
Expand Up @@ -125,7 +125,7 @@ public Property( String name, String groupName, List<BeanLevelInfo> path ) {
this.path = path; this.path = path;
int ac = 0; int ac = 0;
for ( BeanLevelInfo level : path ) { for ( BeanLevelInfo level : path ) {
if ( level.array ) { if ( level.dim != BeanLevelInfo.DIMENSION.NONE ) {
ac++; ac++;
} }
} }
Expand Down
182 changes: 115 additions & 67 deletions engine/src/org/pentaho/di/core/injection/bean/BeanInjector.java
Expand Up @@ -55,15 +55,30 @@ public Object getProperty( Object root, String propName ) throws Exception {
if ( obj == null ) { if ( obj == null ) {
return null; // some value in path is null - return empty return null; // some value in path is null - return empty
} }
if ( s.array ) { switch ( s.dim ) {
int index = extractedIndexes.get( arrIndex++ ); case ARRAY:
if ( Array.getLength( obj ) <= index ) { int indexArray = extractedIndexes.get( arrIndex++ );
return null; if ( Array.getLength( obj ) <= indexArray ) {
} return null;
obj = Array.get( obj, index ); }
if ( obj == null ) { obj = Array.get( obj, indexArray );
return null; // element is empty if ( obj == null ) {
} return null; // element is empty
}
break;
case LIST:
int indexList = extractedIndexes.get( arrIndex++ );
List<?> list = (List<?>) obj;
if ( list.size() <= indexList ) {
return null;
}
obj = list.get( indexList );
if ( obj == null ) {
return null; // element is empty
}
break;
case NONE:
break;
} }
} }
return obj; return obj;
Expand Down Expand Up @@ -112,84 +127,102 @@ private void setProperty( Object root, BeanInjectionInfo.Property prop, int inde
BeanLevelInfo s = prop.path.get( i ); BeanLevelInfo s = prop.path.get( i );
if ( i < prop.path.size() - 1 ) { if ( i < prop.path.size() - 1 ) {
// get path // get path
if ( s.array ) { Object next;
// array switch ( s.dim ) {
Object existArray = extendArray( s, obj, index + 1 ); case ARRAY:
Object next = Array.get( existArray, index ); // get specific element // array
if ( next == null ) { Object existArray = extendArray( s, obj, index + 1 );
// Object can be inner of metadata class. In this case constructor will require parameter next = Array.get( existArray, index ); // get specific element
for ( Constructor<?> c : s.leafClass.getConstructors() ) {
if ( c.getParameterTypes().length == 0 ) {
next = s.leafClass.newInstance();
break;
} else if ( c.getParameterTypes().length == 1 && c.getParameterTypes()[0].isAssignableFrom(
info.clazz ) ) {
next = c.newInstance( root );
break;
}
}
if ( next == null ) { if ( next == null ) {
throw new KettleException( "Empty constructor not found for " + s.leafClass ); next = createObject( s.leafClass, root );
Array.set( existArray, index, next );
} }
Array.set( existArray, index, next ); obj = next;
} break;
obj = next; case LIST:
} else { // list
// plain field List<Object> existList = extendList( s, obj, index + 1 );
if ( s.field != null ) { next = existList.get( index ); // get specific element
Object next = s.field.get( obj );
if ( next == null ) { if ( next == null ) {
next = s.leafClass.newInstance(); next = createObject( s.leafClass, root );
s.field.set( obj, next ); existList.set( index, next );
} }
obj = next; obj = next;
} else if ( s.getter != null ) { break;
Object next = s.getter.invoke( obj ); case NONE:
if ( next == null ) { // plain field
if ( s.setter == null ) { if ( s.field != null ) {
throw new KettleException( "No setter defined for " + root.getClass() ); next = s.field.get( obj );
if ( next == null ) {
next = createObject( s.leafClass, root );
s.field.set( obj, next );
}
obj = next;
} else if ( s.getter != null ) {
next = s.getter.invoke( obj );
if ( next == null ) {
if ( s.setter == null ) {
throw new KettleException( "No setter defined for " + root.getClass() );
}
next = s.leafClass.newInstance();
s.setter.invoke( obj, next );
} }
next = s.leafClass.newInstance(); obj = next;
s.setter.invoke( obj, next ); } else {
throw new KettleException( "No field or getter defined for " + root.getClass() );
} }
obj = next; break;
} else {
throw new KettleException( "No field or getter defined for " + root.getClass() );
}
} }
} else { } else {
// set to latest field // set to latest field
if ( !s.convertEmpty && data.isEmptyValue( dataName ) ) { if ( !s.convertEmpty && data.isEmptyValue( dataName ) ) {
return; return;
} }
if ( s.array ) { if ( s.setter != null ) {
if ( s.field != null ) { Object value = data.getAsJavaType( dataName, s.leafClass, s.converter );
Object existArray = extendArray( s, obj, index + 1 ); // usual setter
Object value = data.getAsJavaType( dataName, s.leafClass, s.converter ); s.setter.invoke( obj, value );
Array.set( existArray, index, value ); } else if ( s.field != null ) {
} else if ( s.setter != null ) { Object value;
Object value = data.getAsJavaType( dataName, s.leafClass, s.converter ); switch ( s.dim ) {
// usual setter case ARRAY:
s.setter.invoke( obj, value ); Object existArray = extendArray( s, obj, index + 1 );
} else { value = data.getAsJavaType( dataName, s.leafClass, s.converter );
throw new KettleException( "No field or setter defined for " + root.getClass() ); Array.set( existArray, index, value );
break;
case LIST:
List<Object> existList = extendList( s, obj, index + 1 );
value = data.getAsJavaType( dataName, s.leafClass, s.converter );
existList.set( index, value );
break;
case NONE:
value = data.getAsJavaType( dataName, s.leafClass, s.converter );
s.field.set( obj, value );
break;
} }
} else { } else {
if ( s.field != null ) { throw new KettleException( "No field or setter defined for " + root.getClass() );
Object value = data.getAsJavaType( dataName, s.leafClass, s.converter );
s.field.set( obj, value );
} else if ( s.setter != null ) {
Object value = data.getAsJavaType( dataName, s.leafClass, s.converter );
// usual setter
s.setter.invoke( obj, value );
} else {
throw new KettleException( "No field or setter defined for " + root.getClass() );
}
} }
} }
} }
} }


private Object createObject( Class<?> clazz, Object root ) throws KettleException {
try {
// Object can be inner of metadata class. In this case constructor will require parameter
for ( Constructor<?> c : clazz.getConstructors() ) {
if ( c.getParameterTypes().length == 0 ) {
return clazz.newInstance();
} else if ( c.getParameterTypes().length == 1 && c.getParameterTypes()[0].isAssignableFrom( info.clazz ) ) {
return c.newInstance( root );
}
}
} catch ( Throwable ex ) {
throw new KettleException( "Can't create object " + clazz, ex );
}
throw new KettleException( "Constructor not found for " + clazz );
}

private Object extendArray( BeanLevelInfo s, Object obj, int newSize ) throws Exception { private Object extendArray( BeanLevelInfo s, Object obj, int newSize ) throws Exception {
Object existArray = s.field.get( obj ); Object existArray = s.field.get( obj );
if ( existArray == null ) { if ( existArray == null ) {
Expand All @@ -203,6 +236,21 @@ private Object extendArray( BeanLevelInfo s, Object obj, int newSize ) throws Ex
existArray = newSized; existArray = newSized;
s.field.set( obj, existArray ); s.field.set( obj, existArray );
} }

return existArray; return existArray;
} }

private List<Object> extendList( BeanLevelInfo s, Object obj, int newSize ) throws Exception {
@SuppressWarnings( "unchecked" )
List<Object> existList = (List<Object>) s.field.get( obj );
if ( existList == null ) {
existList = new ArrayList<>();
s.field.set( obj, existList );
}
while ( existList.size() < newSize ) {
existList.add( null );
}

return existList;
}
} }
23 changes: 19 additions & 4 deletions engine/src/org/pentaho/di/core/injection/bean/BeanLevelInfo.java
Expand Up @@ -25,6 +25,8 @@
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
Expand All @@ -37,6 +39,10 @@
* Storage for one step on the bean deep level. * Storage for one step on the bean deep level.
*/ */
class BeanLevelInfo { class BeanLevelInfo {
enum DIMENSION {
NONE, ARRAY, LIST
};

/** Parent step or null for root. */ /** Parent step or null for root. */
public BeanLevelInfo parent; public BeanLevelInfo parent;
/** Class for step from field or methods. */ /** Class for step from field or methods. */
Expand All @@ -45,8 +51,8 @@ class BeanLevelInfo {
public Field field; public Field field;
/** Getter and setter. */ /** Getter and setter. */
public Method getter, setter; public Method getter, setter;
/** Flag for mark array. */ /** Dimension of level. */
public boolean array; public DIMENSION dim = DIMENSION.NONE;
/** Values converter. */ /** Values converter. */
public InjectionTypeConverter converter; public InjectionTypeConverter converter;
/** False if source empty value shoudn't affect on target field. */ /** False if source empty value shoudn't affect on target field. */
Expand Down Expand Up @@ -92,10 +98,19 @@ protected void introspect( BeanInjectionInfo info, Field[] fields, Method[] meth
leaf.parent = this; leaf.parent = this;
leaf.field = f; leaf.field = f;
if ( f.getType().isArray() ) { if ( f.getType().isArray() ) {
leaf.array = true; leaf.dim = DIMENSION.ARRAY;
leaf.leafClass = f.getType().getComponentType(); leaf.leafClass = f.getType().getComponentType();
} else if ( List.class.isAssignableFrom( f.getType() ) ) {
leaf.dim = DIMENSION.LIST;
Type fieldType = f.getGenericType();
Type listType = ( (ParameterizedType) fieldType ).getActualTypeArguments()[0];
try {
leaf.leafClass = Class.forName( listType.getTypeName(), false, leafClass.getClassLoader() );
} catch ( Throwable ex ) {
throw new RuntimeException( "Can't retrieve type from List for " + f );
}
} else { } else {
leaf.array = false; leaf.dim = DIMENSION.NONE;
leaf.leafClass = f.getType(); leaf.leafClass = f.getType();
} }
if ( annotationInjection != null ) { if ( annotationInjection != null ) {
Expand Down

0 comments on commit 45ed84c

Please sign in to comment.