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;
int ac = 0;
for ( BeanLevelInfo level : path ) {
if ( level.array ) {
if ( level.dim != BeanLevelInfo.DIMENSION.NONE ) {
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 ) {
return null; // some value in path is null - return empty
}
if ( s.array ) {
int index = extractedIndexes.get( arrIndex++ );
if ( Array.getLength( obj ) <= index ) {
return null;
}
obj = Array.get( obj, index );
if ( obj == null ) {
return null; // element is empty
}
switch ( s.dim ) {
case ARRAY:
int indexArray = extractedIndexes.get( arrIndex++ );
if ( Array.getLength( obj ) <= indexArray ) {
return null;
}
obj = Array.get( obj, indexArray );
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;
Expand Down Expand Up @@ -112,84 +127,102 @@ private void setProperty( Object root, BeanInjectionInfo.Property prop, int inde
BeanLevelInfo s = prop.path.get( i );
if ( i < prop.path.size() - 1 ) {
// get path
if ( s.array ) {
// array
Object existArray = extendArray( s, obj, index + 1 );
Object next = Array.get( existArray, index ); // get specific element
if ( next == null ) {
// Object can be inner of metadata class. In this case constructor will require parameter
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;
}
}
Object next;
switch ( s.dim ) {
case ARRAY:
// array
Object existArray = extendArray( s, obj, index + 1 );
next = Array.get( existArray, index ); // get specific element
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;
} else {
// plain field
if ( s.field != null ) {
Object next = s.field.get( obj );
obj = next;
break;
case LIST:
// list
List<Object> existList = extendList( s, obj, index + 1 );
next = existList.get( index ); // get specific element
if ( next == null ) {
next = s.leafClass.newInstance();
s.field.set( obj, next );
next = createObject( s.leafClass, root );
existList.set( index, next );
}
obj = next;
} else if ( s.getter != null ) {
Object next = s.getter.invoke( obj );
if ( next == null ) {
if ( s.setter == null ) {
throw new KettleException( "No setter defined for " + root.getClass() );
break;
case NONE:
// plain field
if ( s.field != null ) {
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();
s.setter.invoke( obj, next );
obj = next;
} else {
throw new KettleException( "No field or getter defined for " + root.getClass() );
}
obj = next;
} else {
throw new KettleException( "No field or getter defined for " + root.getClass() );
}
break;
}
} else {
// set to latest field
if ( !s.convertEmpty && data.isEmptyValue( dataName ) ) {
return;
}
if ( s.array ) {
if ( s.field != null ) {
Object existArray = extendArray( s, obj, index + 1 );
Object value = data.getAsJavaType( dataName, s.leafClass, s.converter );
Array.set( existArray, index, 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() );
if ( s.setter != null ) {
Object value = data.getAsJavaType( dataName, s.leafClass, s.converter );
// usual setter
s.setter.invoke( obj, value );
} else if ( s.field != null ) {
Object value;
switch ( s.dim ) {
case ARRAY:
Object existArray = extendArray( s, obj, index + 1 );
value = data.getAsJavaType( dataName, s.leafClass, s.converter );
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 {
if ( s.field != null ) {
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() );
}
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 {
Object existArray = s.field.get( obj );
if ( existArray == null ) {
Expand All @@ -203,6 +236,21 @@ private Object extendArray( BeanLevelInfo s, Object obj, int newSize ) throws Ex
existArray = newSized;
s.field.set( obj, 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.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -37,6 +39,10 @@
* Storage for one step on the bean deep level.
*/
class BeanLevelInfo {
enum DIMENSION {
NONE, ARRAY, LIST
};

/** Parent step or null for root. */
public BeanLevelInfo parent;
/** Class for step from field or methods. */
Expand All @@ -45,8 +51,8 @@ class BeanLevelInfo {
public Field field;
/** Getter and setter. */
public Method getter, setter;
/** Flag for mark array. */
public boolean array;
/** Dimension of level. */
public DIMENSION dim = DIMENSION.NONE;
/** Values converter. */
public InjectionTypeConverter converter;
/** 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.field = f;
if ( f.getType().isArray() ) {
leaf.array = true;
leaf.dim = DIMENSION.ARRAY;
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 {
leaf.array = false;
leaf.dim = DIMENSION.NONE;
leaf.leafClass = f.getType();
}
if ( annotationInjection != null ) {
Expand Down

0 comments on commit 45ed84c

Please sign in to comment.