/*******************************************************************************
* Copyright (c) 2012 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.xpect.setup;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.JvmOperation;
import org.xpect.AbstractComponent;
import org.xpect.Assignment;
import org.xpect.BooleanLiteral;
import org.xpect.ClassLiteral;
import org.xpect.Component;
import org.xpect.IntLiteral;
import org.xpect.StringLiteral;
import org.xpect.Value;
import org.xpect.util.IJavaReflectAccess;
import com.google.common.base.Joiner;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public class SetupInitializer<T> implements ISetupInitializer<T> {
private final AbstractComponent rootInstance;
public SetupInitializer(AbstractComponent rootInstance) {
this.rootInstance = rootInstance;
}
protected Object create(BooleanLiteral val) {
return val.isValue();
}
protected Object create(ClassLiteral val) {
return IJavaReflectAccess.INSTANCE.getRawType(val.getType());
}
protected Object create(Component val) {
Class<?> type = IJavaReflectAccess.INSTANCE.getRawType(val.getComponentClass());
try {
Object[] params = new Object[val.getParameters().size()];
for (int i = 0; i < val.getParameters().size(); i++)
params[i] = create(val.getParameters().get(i));
Constructor<?> constructor = findConstructor(type, params);
Object result = constructor.newInstance(params);
initialize(result, val);
return result;
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
protected Object create(IntLiteral val) {
return val.getValue();
}
protected Object create(StringLiteral val) {
return val.getValue();
}
protected Object create(Value val) {
if (val instanceof Component)
return create((Component) val);
if (val instanceof BooleanLiteral)
return create((BooleanLiteral) val);
if (val instanceof StringLiteral)
return create((StringLiteral) val);
if (val instanceof IntLiteral)
return create((IntLiteral) val);
if (val instanceof ClassLiteral)
return create((ClassLiteral) val);
return null;
}
protected Constructor<?> findConstructor(Class<?> clazz, Object[] params) {
START: for (Constructor<?> c : clazz.getConstructors())
if (c.getParameterTypes().length == params.length) {
for (int i = 0; i < params.length; i++)
if (!c.getParameterTypes()[i].isInstance(params[i]))
continue START;
return c;
}
// Support for single Varargs: exactly one array-parameter in constructor.
START: for (Constructor<?> c : clazz.getConstructors())
if (c.getParameterTypes().length == 1) {
Class<?> aType = c.getParameterTypes()[0];
if (!aType.isArray())
continue START;
Class<?> cType = aType.getComponentType();
for (int i = 0; i < params.length; i++)
if (!cType.isInstance(params[i]))
continue START;
return c;
}
throw new RuntimeException("Type " + clazz + " has no constructor suitable for params " + Joiner.on(", ").join(params));
}
protected Method findMethod(Object target, Assignment assignment) {
JvmOperation result = assignment.getDeclaredTarget();
if (result != null) {
if (result.eIsProxy())
throw new RuntimeException("unresolved proxy:" + result);
return IJavaReflectAccess.INSTANCE.getMethod(result);
}
Value val = assignment.getValue();
if (val instanceof Component) {
JvmDeclaredType toBeAssigned = ((Component) val).getComponentClass();
if (toBeAssigned != null && !toBeAssigned.eIsProxy()) {
Class<?> toBeAssignedJava = IJavaReflectAccess.INSTANCE.getRawType(toBeAssigned);
if (toBeAssignedJava != null)
for (Method candidate : target.getClass().getMethods())
if ("add".equals(candidate.getName()) && candidate.getParameterTypes().length == 1) {
Class<?> parameterType = candidate.getParameterTypes()[0];
if (parameterType.isAssignableFrom(toBeAssignedJava))
return candidate;
}
}
}
return null;
}
public AbstractComponent getRootInstance() {
return rootInstance;
}
protected void initialize(Object obj, AbstractComponent init) {
for (Assignment a : init.getAssignments()) {
Method m = findMethod(obj, a);
if (m != null) {
Object object = create(a.getValue());
try {
m.invoke(obj, object);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
public void initialize(T object) {
if (rootInstance != null)
initialize(object, rootInstance);
}
}