/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.portal.datasource.handler.extension;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
import com.enonic.cms.framework.util.JDOMUtil;
import com.enonic.cms.api.plugin.ext.FunctionLibrary;
import com.enonic.cms.core.plugin.ext.FunctionLibraryExtensions;
import com.enonic.cms.core.portal.datasource.DataSourceException;
import com.enonic.cms.core.portal.datasource.handler.DataSourceRequest;
import com.enonic.cms.core.portal.datasource.handler.base.ParameterConverter;
import com.enonic.cms.core.portal.datasource.handler.base.SimpleDataSourceHandler;
@Component("ds.InvokeExtensionHandler")
public final class InvokeExtensionHandler
extends SimpleDataSourceHandler
{
private FunctionLibraryExtensions extensions;
private ParameterConverter parameterConverter;
public InvokeExtensionHandler()
{
super( "invokeExtension" );
parameterConverter = ParameterConverter.getInstance();
}
@Override
public Document handle( final DataSourceRequest req )
throws Exception
{
final String name = requiredParam( req, "name" );
final String[] nameParts = name.split( "\\." );
if ( nameParts.length != 2 )
{
throw new DataSourceException( "Extension [{0}] was not found", name );
}
final String namespace = nameParts[0];
final String methodName = nameParts[1];
final FunctionLibrary library = extensions.getByName( namespace );
if ( library == null )
{
throw new DataSourceException( "Function library [{0}] was not found", namespace );
}
final String[] parameterValues = getParameterValues( req );
final int parameterCount = parameterValues.length;
final List<Method> methods = findMethod( library, methodName, parameterCount );
if ( methods.isEmpty() )
{
throw new DataSourceException( "Method [{0}] with {1} parameters was not found in function library [{2}]", methodName,
parameterCount == 0 ? "no" : parameterCount, namespace );
}
if ( methods.size() != 1 )
{
throw new DataSourceException( "Multiple methods [{0}] with {1} parameters found in function library [{2}]", methodName,
parameterCount == 0 ? "no" : parameterCount, namespace );
}
return wrapResultValue( invokeMethod( methods.get( 0 ), library.getTarget(), parameterValues ) );
}
private String[] getParameterValues( final DataSourceRequest req )
{
final List<String> parameterValues = Lists.newArrayList();
final Map<String, String> params = req.getParams();
int i = 1;
while ( params.containsKey( "param" + i ) )
{
parameterValues.add( params.get( "param" + i ) );
i++;
}
return parameterValues.toArray( new String[parameterValues.size()] );
}
private Object invokeMethod( final Method method, final Object target, final String... arguments )
throws InvocationTargetException, IllegalAccessException
{
final Object[] methodArguments = convertArguments( arguments, method.getParameterTypes() );
return method.invoke( target, methodArguments );
}
private Object[] convertArguments( final String[] arguments, final Class<?>[] parameterTypes )
{
final Object[] convertedArgs = new Object[arguments.length];
for ( int i = 0; i < arguments.length; i++ )
{
convertedArgs[i] = parameterConverter.convert( arguments[i], parameterTypes[i] );
}
return convertedArgs;
}
private Document wrapResultValue( final Object value )
{
if ( value instanceof Document )
{
return (Document) value;
}
else if ( value instanceof org.w3c.dom.Document )
{
return JDOMUtil.toDocument( (org.w3c.dom.Document) value );
}
else
{
final Element root = new Element( "value" );
if ( value != null )
{
root.setText( value.toString() );
}
return new Document( root );
}
}
private List<Method> findMethod( final FunctionLibrary library, final String methodName, final int parameterCount )
{
final Class<?> targetClass = library.getTargetClass();
final List<Method> methods = Lists.newArrayList();
for ( final Method method : targetClass.getMethods() )
{
if ( method.getName().equals( methodName ) && method.getParameterTypes().length == parameterCount )
{
methods.add( method );
}
}
return methods;
}
@Autowired
public void setExtensions( final FunctionLibraryExtensions extensions )
{
this.extensions = extensions;
}
}