All Downloads are FREE. Search and download functionalities are using the official Maven repository.

java.lang.reflect.Method Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2014 Fernando Petrola
 *
 * 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 java.lang.reflect;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;

import com.dragome.commons.compiler.annotations.CompilerType;
import com.dragome.commons.compiler.annotations.DragomeCompilerSettings;
import com.dragome.commons.compiler.annotations.MethodAlias;
import com.dragome.commons.javascript.ScriptHelper;
import com.dragome.helpers.DragomeEntityManager;
import com.dragome.services.ServiceInvocation;
import com.dragome.services.WebServiceLocator;
import com.dragome.web.annotations.ClientSideMethod;
import com.dragome.web.debugging.JavascriptReference;
import com.dragome.web.dispatcher.JavaRefId;
import com.dragome.web.enhancers.jsdelegate.JsCast;

import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

@DragomeCompilerSettings(CompilerType.Standard)
public final class Method extends Executable
{
	protected String signature;
	protected Class cls;
	protected boolean accessible;
	protected Class[] parametersTypes;
	protected Class returnType;
	private int modifiers;

	public Method(Class newCls, String theSignature, int modifiers)
	{
		signature= theSignature;
		cls= newCls;
		this.modifiers= modifiers;
	}

	public String getName()
	{
		String name= signature.substring(1, signature.lastIndexOf("$"));

		int parametersStart= name.indexOf("___");
		if (parametersStart != -1)
			return name.substring(0, parametersStart);
		else
			return name;
	}

	public Class[] getParameterTypes()
	{
		String signatureWithNoReturnType= signature.substring(0, signature.lastIndexOf("$"));
		String[] parameters= signatureWithNoReturnType.replaceAll("____", "__").replaceAll("___", "__").split("__");
		List> result= new ArrayList>();

		if (parameters.length > 1)
		{
			for (int i= 1; i < parameters.length; i++)
			{
				String typeName= parameters[i];
				if (typeName.trim().length() > 0 && !typeName.contains("$"))
				{
					//			typeName= boxTypes(typeName);

					Class parameterType= Object.class;
					try
					{
						parameterType= Class.forName(fixArrayClassName(typeName));
					}
					catch (Exception e)
					{
					}

					result.add(parameterType);
				}
			}
			return result.toArray(new Class[0]);
		}
		else
			return new Class[0];
	}

	public static String boxTypes(String typeName)
	{
		if ("boolean".equals(typeName))
			return "java.lang.Boolean";
		else if ("int".equals(typeName))
			return "java.lang.Integer";
		else if ("long".equals(typeName))
			return "java.lang.Long";
		else if ("short".equals(typeName))
			return "java.lang.Short";
		else if ("float".equals(typeName))
			return "java.lang.Float";
		else if ("double".equals(typeName))
			return "java.lang.Double";
		else if ("byte".equals(typeName))
			return "java.lang.Byte";
		else if ("char".equals(typeName))
			return "java.lang.Character";

		return typeName;
	}

	public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		boxArguments(getParameterTypes(), args);

		Object result= null;
		if (obj == null)
		{
			ScriptHelper.put("relatedClass", cls, this);
			obj= ScriptHelper.eval("relatedClass.$$$nativeClass", this);
		}

		ScriptHelper.put("obj", obj, this);
		ScriptHelper.put("args", args, this);
		ScriptHelper.put("sig", this.signature, this);

		Object instanceMethod= ScriptHelper.eval("obj[sig]", this);

		if (instanceMethod == null)
			result= ScriptHelper.eval("obj.clazz.constructor[sig](args)", this);
		else
			result= ScriptHelper.eval("obj[sig].apply(obj, args)", this);

		result= adaptResult(result, getReturnType());
		return result;
	}

	@MethodAlias(local_alias= "apply")
	public Object javaCall(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		WebServiceLocator webServiceLocator= WebServiceLocator.getInstance();
		boolean executeAtServer= webServiceLocator.isRemoteDebugging() && getAnnotation(ClientSideMethod.class) == null;

		Class[] parameterTypes= getParameterTypes();
		Object[] typedArguments= new Object[parameterTypes.length];

		for (int j= 0; j < args.length; j++)
		{
			Object object= args[j];
			Object typedArgument= JsCast.castTo(object, parameterTypes[j]);
			typedArguments[j]= typedArgument;
			//			ScriptHelper.put("a", object, this);
			//			ScriptHelper.eval("console.log(a)", this);
		}

		if (executeAtServer)
		{
			ScriptHelper.put("obj", obj, this);
			String javaRefIdString= (String) ScriptHelper.eval("obj.javaRefId", this);

			JavaRefId javaRefId= new JavaRefId(javaRefIdString);

			List typedParametersRefs= new ArrayList<>();

			for (int i= 0; i < typedArguments.length; i++)
			{
				if (parameterTypes[i].isInterface())
				{
					String id= DragomeEntityManager.add(typedArguments[i]);
					typedParametersRefs.add(new JavascriptReference(parameterTypes[i].getName(), id));
				}
				else
					typedParametersRefs.add(args[i]);
			}

			ServiceInvocation serviceInvocation= new ServiceInvocation(getDeclaringClass(), this, typedParametersRefs, javaRefIdString);

			webServiceLocator.getEventDispatcher().callJavaMethod(serviceInvocation);
			return null;
		}
		else
			return invoke(obj, typedArguments);
	}

	public static void boxArguments(Class[] parameterTypes, Object... args)
	{
		ScriptHelper.put("args", args, null);

		for (int i= 0; i < parameterTypes.length; i++)
		{
			ScriptHelper.put("parameterType", parameterTypes[i], null);

			if (args[i] != null)
			{
				if (ScriptHelper.evalBoolean("parameterType.realName == 'boolean'", null) && args[i] instanceof Boolean)
					ScriptHelper.put("argValue", Boolean.parseBoolean(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'int'", null) && args[i] instanceof Integer)
					ScriptHelper.put("argValue", Integer.parseInt(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'long'", null) && args[i] instanceof Long)
					ScriptHelper.put("argValue", Long.parseLong(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'short'", null) && args[i] instanceof Short)
					ScriptHelper.put("argValue", Short.parseShort(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'float'", null) && args[i] instanceof Float)
					ScriptHelper.put("argValue", Float.parseFloat(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'double'", null) && args[i] instanceof Double)
					ScriptHelper.put("argValue", Double.parseDouble(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'byte'", null) && args[i] instanceof Byte)
					ScriptHelper.put("argValue", Byte.parseByte(args[i].toString()), null);
				else if (ScriptHelper.evalBoolean("parameterType.realName == 'char'", null) && args[i] instanceof Character)
					ScriptHelper.put("argValue", ((Character) args[i]).charValue(), null);
				else
					ScriptHelper.put("argValue", args[i], null);

				ScriptHelper.put("i", i, null);
				ScriptHelper.eval("args[i]= argValue", null);
			}
		}
	}

	public static Object adaptResult(Object result, Class returnType)
	{
		ScriptHelper.put("result", result, null);

		try
		{
			Class currentReturnType= returnType;

			if (currentReturnType != null)
				if (currentReturnType.equals(Boolean.class))
					result= result instanceof Boolean ? result : (ScriptHelper.evalBoolean("result", null) ? Boolean.TRUE : Boolean.FALSE);
				else if (currentReturnType.equals(Integer.class))
					result= Integer.parseInt(ScriptHelper.evalInt("result", null) + "");
				else if (currentReturnType.equals(Long.class))
					result= Long.parseLong(ScriptHelper.evalInt("result", null) + "");
				else if (currentReturnType.equals(Short.class))
					result= Short.parseShort(ScriptHelper.evalInt("result", null) + "");
				else if (currentReturnType.equals(Float.class))
					result= Float.parseFloat(ScriptHelper.evalFloat("result", null) + "");
				else if (currentReturnType.equals(Double.class))
					result= Double.parseDouble(ScriptHelper.evalDouble("result", null) + "");
				else if (currentReturnType.equals(Byte.class))
					result= Byte.valueOf((byte) ScriptHelper.evalChar("result", null));
				else if (currentReturnType.equals(Character.class))
					result= Character.valueOf((ScriptHelper.eval("result", null) + "").charAt(0));
		}
		catch (Exception e)
		{
			System.out.println("adaptResult failed");
		}
		return result;
	}
	public boolean isAnnotationPresent(Class annotation)
	{
		return false; //throw new UnsupportedOperationException();
	}

	public String toString()
	{
		return signature;
	}

	public void setAccessible(boolean accessible)
	{
		this.accessible= accessible;
	}

	public int getModifiers()
	{
		return modifiers;
	}

	public  T getAnnotation(Class annotationClass)
	{
		return Class.getAnnotationInternal(getDeclaringClass(), annotationClass, getName(), null, null);
	}

	public Class getReturnType()
	{
		if (returnType == null)
		{
			try
			{
				String returnTypeString= signature.substring(signature.lastIndexOf("$") + 1);
				returnTypeString= fixArrayClassName(returnTypeString);
				//				returnTypeString= boxTypes(returnTypeString);
				returnType= Class.forName(returnTypeString);
			}
			catch (ClassNotFoundException e)
			{
				throw new RuntimeException(e);
			}
		}
		return returnType;
	}

	private String fixArrayClassName(String methodName)
	{
		if (methodName.endsWith("[]"))
			methodName= "[L" + methodName.substring(0, methodName.length() - 2) + ";";
		return methodName;
	}

	public Type[] getGenericParameterTypes()
	{
		return getParameterTypes();
	}

	public Class getDeclaringClass()
	{
		return cls;
	}

	public Type getGenericReturnType()
	{
		Class declaringClass= getDeclaringClass();
		ScriptHelper.put("declaringClass", declaringClass, this);
		if (ScriptHelper.evalBoolean("declaringClass.$$$nativeClass.$$$$signatures ", this))
		{
			String genericSignature= (String) ScriptHelper.eval("declaringClass.$$$nativeClass.$$$$signatures[this.$$$signature]", this);
			genericSignature= genericSignature.replaceAll(".*;", "");
			genericSignature= genericSignature.replaceAll("/", "_");

			return new ParameterizedTypeImpl(genericSignature);
		}
		else
			return getReturnType();
	}

	public Object[] boxParameters(Object[] args)
	{
		Class[] parameterTypes= getParameterTypes();
		for (int i= 0; i < parameterTypes.length; i++)
		{
			if (parameterTypes[i].isPrimitive())
			{
				ScriptHelper.put("primitiveValue", args[i], null);
				String stringValue= (String) ScriptHelper.eval("primitiveValue.toString()", this);

				if (parameterTypes[i].equals(int.class))
					args[i]= new Integer(stringValue);
				else if (parameterTypes[i].equals(long.class))
					args[i]= new Long(stringValue);
				else if (parameterTypes[i].equals(float.class))
					args[i]= new Float(stringValue);
				else if (parameterTypes[i].equals(double.class))
					args[i]= new Double(stringValue);
				else if (parameterTypes[i].equals(short.class))
					args[i]= new Short(stringValue);
				else if (parameterTypes[i].equals(char.class))
					args[i]= new Character(stringValue.charAt(0));
				else if (parameterTypes[i].equals(byte.class))
					args[i]= new Byte(stringValue);
				else if (parameterTypes[i].equals(boolean.class))
					args[i]= new Boolean(stringValue);
			}
		}
		return args;
	}

	public Parameter[] getParameters()
	{
		List result= new ArrayList<>();

		Class[] parameterTypes= getParameterTypes();
		for (int i= 0; i < parameterTypes.length; i++)
			result.add(new Parameter(getDeclaringClass(), this, "arg" + i, 0, i));

		return result.toArray(new Parameter[0]);
	}

	public boolean isSynthetic()
	{
		return false;
	}

	public TypeVariable[] getTypeParameters()
	{
		return new TypeVariable[0];
	}

	public Annotation[][] getParameterAnnotations()
	{
		return new Annotation[0][0];
	}

	public boolean isBridge()
	{
		return (getModifiers() & Modifier.BRIDGE) != 0;
	}
}