/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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.pentaho.di.core.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import org.pentaho.di.core.Condition;
import org.pentaho.di.core.database.DatabaseInterface;
import org.pentaho.di.core.plugins.JobEntryPluginType;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.plugins.StepPluginType;
public class StringSearcher {
private static final String LOCAL_PACKAGE = "org.pentaho.di";
private static final String[] JAVA_PACKAGES = new String[] { "java.util", };
private static List<String> stepPluginPackages;
private static List<String> jobEntryPluginPackages;
public static final void findMetaData( Object object, int level, List<StringSearchResult> stringList,
Object parentObject, Object grandParentObject ) {
// System.out.println(Const.rightPad(" ", level)+"Finding strings in "+object.toString());
if ( level > 5 ) {
return;
}
PluginRegistry registry = PluginRegistry.getInstance();
if ( stepPluginPackages == null ) {
stepPluginPackages = registry.getPluginPackages( StepPluginType.class );
}
if ( jobEntryPluginPackages == null ) {
jobEntryPluginPackages = registry.getPluginPackages( JobEntryPluginType.class );
}
Class<? extends Object> baseClass = object.getClass();
Field[] fields = baseClass.getDeclaredFields();
for ( int i = 0; i < fields.length; i++ ) {
Field field = fields[i];
boolean processThisOne = true;
if ( ( field.getModifiers() & Modifier.FINAL ) > 0 ) {
processThisOne = false;
}
if ( ( field.getModifiers() & Modifier.STATIC ) > 0 ) {
processThisOne = false;
}
// Investigate only if we're dealing with a sanctioned package.
// A sanctioned package is either the local package (org.pentaho.di) or
// a package of one of the plugins.
//
boolean sanctionedPackage = false;
if ( field.toString().indexOf( LOCAL_PACKAGE ) >= 0 ) {
sanctionedPackage = true;
}
for ( int x = 0; x < JAVA_PACKAGES.length && !sanctionedPackage; x++ ) {
if ( field.toString().indexOf( JAVA_PACKAGES[x] ) >= 0 ) {
sanctionedPackage = true;
}
}
for ( int x = 0; x < stepPluginPackages.size() && !sanctionedPackage; x++ ) {
if ( field.toString().indexOf( stepPluginPackages.get( x ) ) >= 0 ) {
sanctionedPackage = true;
}
}
for ( int x = 0; x < jobEntryPluginPackages.size() && !sanctionedPackage; x++ ) {
if ( field.toString().indexOf( jobEntryPluginPackages.get( x ) ) >= 0 ) {
sanctionedPackage = true;
}
}
if ( !sanctionedPackage ) {
processThisOne = false; // Stay in the sanctioned code-base.
}
// Dig into the metadata from here...
//
if ( processThisOne ) {
try {
Object obj = field.get( object );
if ( obj != null ) {
stringSearchInObject( obj, level, stringList, parentObject, grandParentObject, field );
}
} catch ( IllegalAccessException e ) {
// OK, it's private, let's see if we can go there later on using
// getters and setters...
// fileName becomes: getFileName();
// OK, how do we get the value now?
try {
Method method = findMethod( baseClass, field.getName() );
if ( method != null ) {
// String fullMethod =
// baseClass.getName()+"."+method.getName()+"()";
Object string = method.invoke( object, (Object[]) null );
if ( string != null ) {
stringSearchInObject( string, level, stringList, parentObject, grandParentObject, field );
}
}
} catch ( Throwable ex ) {
// Ignore this error silently. If we can't access the method there
// is nothing you can do about it.
}
}
}
}
}
private static void stringSearchInObject( Object obj, int level, List<StringSearchResult> stringList,
Object parentObject, Object grandParentObject, Field field ) {
if ( obj instanceof String ) {
// OK, let's add the String
stringList.add( new StringSearchResult( (String) obj, parentObject, grandParentObject, field.getName() ) );
} else if ( obj instanceof String[] ) {
String[] array = (String[]) obj;
for ( int x = 0; x < array.length; x++ ) {
if ( array[x] != null ) {
stringList.add( new StringSearchResult( array[x], parentObject, grandParentObject, field.getName()
+ " #" + ( x + 1 ) ) );
}
}
} else if ( obj instanceof Boolean ) {
// OK, let's add the String
stringList.add( new StringSearchResult( ( (Boolean) obj ).toString(), parentObject, grandParentObject, field
.getName()
+ " (Boolean)" ) );
} else if ( obj instanceof Condition ) {
stringList.add( new StringSearchResult(
( (Condition) obj ).toString(), parentObject, grandParentObject, field.getName() + " (Condition)" ) );
} else if ( obj instanceof DatabaseInterface ) {
// Make sure we read the attributes. This is not picked up by default. (getDeclaredFields doesn't pick up
// inherited fields)
//
DatabaseInterface databaseInterface = (DatabaseInterface) obj;
findMapMetaData(
databaseInterface.getAttributes(), level + 1, stringList, parentObject, grandParentObject, field );
findMetaData( obj, level + 1, stringList, parentObject, grandParentObject );
} else if ( obj instanceof Map ) {
findMapMetaData( (Map<?, ?>) obj, level, stringList, parentObject, grandParentObject, field );
} else if ( obj instanceof Object[] ) {
for ( int j = 0; j < ( (Object[]) obj ).length; j++ ) {
findMetaData( ( (Object[]) obj )[j], level + 1, stringList, parentObject, grandParentObject );
}
} else {
findMetaData( obj, level + 1, stringList, parentObject, grandParentObject );
}
}
private static void findMapMetaData( Map<?, ?> map, int level, List<StringSearchResult> stringList,
Object parentObject, Object grandParentObject, Field field ) {
for ( Object key : map.keySet() ) {
Object value = map.get( key );
if ( key != null ) {
stringList.add( new StringSearchResult( key.toString(), parentObject, grandParentObject, field.getName()
+ " (Map key)" ) );
}
if ( value != null ) {
stringList.add( new StringSearchResult( value.toString(), parentObject, grandParentObject, field.getName()
+ " (Map value)" ) );
}
}
}
private static Method findMethod( Class<? extends Object> baseClass, String name ) {
// baseClass.getMethod(methodName[m], null);
Method[] methods = baseClass.getDeclaredMethods();
Method method = null;
// getName()
if ( method == null ) {
String getter = constructGetter( name );
method = searchGetter( getter, baseClass, methods );
}
// isName()
if ( method == null ) {
String getter = constructIsGetter( name );
method = searchGetter( getter, baseClass, methods );
}
// name()
if ( method == null ) {
String getter = name;
method = searchGetter( getter, baseClass, methods );
}
return method;
}
private static Method searchGetter( String getter, Class<?> baseClass, Method[] methods ) {
Method method = null;
try {
method = baseClass.getMethod( getter );
} catch ( Exception e ) {
// Nope try case insensitive.
for ( int i = 0; i < methods.length; i++ ) {
String methodName = methods[i].getName();
if ( methodName.equalsIgnoreCase( getter ) ) {
return methods[i];
}
}
}
return method;
}
public static final String constructGetter( String name ) {
StringBuilder buf = new StringBuilder();
buf.append( "get" );
buf.append( name.substring( 0, 1 ).toUpperCase() );
buf.append( name.substring( 1 ) );
return buf.toString();
}
public static final String constructIsGetter( String name ) {
StringBuilder buf = new StringBuilder();
buf.append( "is" );
buf.append( name.substring( 0, 1 ).toUpperCase() );
buf.append( name.substring( 1 ) );
return buf.toString();
}
}