package gw.vark.typeloader;
import gw.lang.GosuShop;
import gw.lang.function.IFunction1;
import gw.lang.reflect.*;
import gw.lang.reflect.java.CustomTypeInfoBase;
import gw.lang.reflect.java.JavaTypes;
import gw.util.GosuExceptionUtil;
import gw.util.Pair;
import gw.vark.Aardvark;
import org.apache.tools.ant.*;
import org.apache.tools.ant.taskdefs.Antlib;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.ResourceCollection;
import org.apache.tools.ant.types.resources.URLResource;
import org.apache.tools.ant.util.FileUtils;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
super(owner);
initTasks(resourceName);
addConstructor(new ConstructorInfoBuilder()
.withConstructorHandler(new IConstructorHandler() {
@Override
return new Object();
}
})
.build(this));
}
private void initTasks(String resourceName) {
List<Pair<String, String>> listing;
if (resourceName.endsWith(".properties")) {
listing = readTaskListingFromPropertiesFile(resourceName);
} else if (resourceName.endsWith(".xml")) {
listing = readTaskListingFromAntlib(resourceName);
} else {
throw new IllegalArgumentException("resourceName must have suffix .resource or .xml");
}
for (Pair<String, String> entry : listing) {
String taskName = entry.getFirst();
String taskClassName = entry.getSecond();
addTaskAsMethod(taskName, taskClassName);
}
}
@SuppressWarnings({"unchecked"})
private static Class<?
extends Task>
getTaskClass(String taskClassName)
throws ClassNotFoundException {
return (Class<? extends Task>) Class.forName(taskClassName);
}
URL url = AntlibTypeLoader.class.getClassLoader().getResource(resourceName);
InputStream in = null;
try {
in = url.openStream();
if (in == null) {
AntlibTypeLoader.log("Could not load definitions from " + url, Project.MSG_WARN);
}
Properties tasks = new Properties();
tasks.load(in);
List<Pair<String, String>> listing = new ArrayList<Pair<String, String>>();
for (Map.Entry<Object, Object> entry : tasks.entrySet()) {
listing.add(new Pair<String, String>((String) entry.getKey(), (String) entry.getValue()));
}
return listing;
} catch (IOException e) {
throw GosuExceptionUtil.forceThrow(e);
} finally {
FileUtils.close(in);
}
}
URL antlibUrl = AntlibTypeLoader.class.getClassLoader().getResource(resourceName);
URLResource antlibResource = new URLResource(antlibUrl);
ProjectHelperRepository helperRepository = ProjectHelperRepository.getInstance();
ProjectHelper parser = helperRepository.getProjectHelperForAntlib(antlibResource);
UnknownElement ue = parser.parseAntlibDescriptor(AntlibTypeLoader.NULL_PROJECT, antlibResource);
if (!ue.getTag().equals(Antlib.TAG)) {
throw new BuildException("Unexpected tag " + ue.getTag() + " expecting " + Antlib.TAG, ue.getLocation());
}
List<Pair<String, String>> listing = new ArrayList<Pair<String, String>>();
for (Object childObj : ue.getChildren()) {
UnknownElement child = (UnknownElement) childObj;
if (child.getTag().equals("taskdef")) {
Map attributes = child.getWrapper().getAttributeMap();
listing.add(new Pair<String, String>((String)attributes.get("name"), (String)attributes.get("classname")));
}
}
return listing;
}
protected final void addTaskAsMethod(String taskName, String taskClassName) {
MethodInfoBuilder methodInfoBuilder = new MethodInfoBuilder()
.withName(taskName)
.withStatic();
try {
Class<? extends Task> taskClass = getTaskClass(taskClassName);
TaskMethods taskMethods = processTaskMethods(taskClass);
methodInfoBuilder
.withReturnType(taskClass)
.withParameters(taskMethods.getParameterInfoBuilders())
.withCallHandler(new TaskMethodCallHandler(taskName, taskClass, taskMethods));
} catch (ClassNotFoundException cnfe) {
badTask(taskName, methodInfoBuilder, cnfe);
} catch (NoClassDefFoundError ncdfe) {
badTask(taskName, methodInfoBuilder, ncdfe);
}
addMethod(methodInfoBuilder.build(this));
}
private void badTask(String taskName, MethodInfoBuilder methodInfoBuilder, Throwable t) {
String message = "Task " + taskName + " is unavailable, due to " + t;
AntlibTypeLoader.log(message, Project.MSG_VERBOSE);
methodInfoBuilder.withDescription(message);
}
IntrospectionHelper helper = IntrospectionHelper.getHelper(taskClass);
TaskMethods taskMethods = new TaskMethods(taskClass);
for (Enumeration en = helper.getAttributes(); en.hasMoreElements();) {
String attributeName = (String) en.nextElement();
taskMethods.add(new TaskSetter(attributeName, helper.getAttributeType(attributeName)));
}
for (Enumeration en = helper.getNestedElements(); en.hasMoreElements();) {
String elementName = (String) en.nextElement();
Method elementMethod = helper.getElementMethod(elementName);
if (elementMethod.getName().startsWith("add") && elementMethod.getParameterTypes().length == 1) {
taskMethods.add(new TaskAdder(elementName, helper.getElementType(elementName)));
}
else {
taskMethods.add(new TaskCreator(elementName, helper.getElementType(elementName)));
}
}
for (Object methodObj : helper.getExtensionPoints()) {
Method method = (Method) methodObj;
if (method.getName().equals("add") && method.getParameterTypes().length == 1 && method.getParameterTypes()[0] == ResourceCollection.class) {
taskMethods.add(new CustomTaskMethod(ResourceCollection.class, "resources", method));
break;
}
}
return taskMethods;
}
private final Class<? extends Task> _taskClass;
private final TreeMap<String, TaskMethod> _methodMap = new TreeMap<String, TaskMethod>();
_taskClass = taskClass;
}
void add(TaskMethod taskMethod) {
String paramName = taskMethod.getParamName();
if (_methodMap.containsKey(paramName)) {
throw new IllegalArgumentException("cannot add a duplicate param name \"" + paramName + "\" for task class " + _taskClass.getName());
}
_methodMap.put(paramName, taskMethod);
}
List<ParameterInfoBuilder> builders = new ArrayList<ParameterInfoBuilder>();
for (TaskMethod taskMethod : _methodMap.values()) {
builders.add(taskMethod.createParameterInfoBuilder());
}
return builders.toArray(new ParameterInfoBuilder[_methodMap.size()]);
}
ArrayList<TaskMethod>
asList() {
return new ArrayList<TaskMethod>(_methodMap.values());
}
}
protected final String _helperKey;
protected final Class _type;
protected TaskMethod(String helperKey, Class type) {
_helperKey = helperKey;
_type = type;
}
abstract void invoke(Task taskInstance, Object arg, IntrospectionHelper helper);
return JavaTypes.LIST().getParameterizedType( TypeSystem.get( parameterType ) );
}
try {
Class<?> clazz = Class.forName("gw.internal.gosu.parser.expressions.BlockType");
Constructor<?> ctor = clazz.getConstructor(IType.class, IType[].class, List.class, List.class);
IType blkType = (IType) ctor.newInstance(JavaTypes.pVOID(), new IType[]{TypeSystem.get(parameterType)},
Arrays.asList("arg"), Collections.<Object>emptyList());
return JavaTypes.LIST().getGenericType().getParameterizedType(blkType);
} catch (Exception e) {
throw GosuExceptionUtil.forceThrow(e);
}
}
}
private static class TaskSetter extends TaskMethod {
private enum TypeCategory {
PRIMITIVE,
ENUM,
PLAIN
}
private TypeCategory _typeCategory;
super(helperKey, type);
if (type.isPrimitive()) {
_typeCategory = TypeCategory.PRIMITIVE;
}
else if (EnumeratedAttribute.class.isAssignableFrom(type)) {
_typeCategory = TypeCategory.ENUM;
}
else {
_typeCategory = TypeCategory.PLAIN;
}
}
@Override
return _helperKey;
}
@Override
return new ParameterInfoBuilder()
.withName(getParamName())
.withType(makeParamType(_type))
.withDefValue(GosuShop.getNullExpressionInstance());
}
@Override
void invoke(Task taskInstance, Object arg, IntrospectionHelper helper) {
if (TypeCategory.ENUM == _typeCategory) {
arg = EnumeratedAttribute.getInstance(_type, arg.toString().toLowerCase());
}
helper.setAttribute(null, taskInstance, _helperKey, arg);
}
switch (_typeCategory) {
case PRIMITIVE:
return TypeSystem.getBoxType(TypeSystem.get(clazz));
case ENUM:
String enumName = TypeSystem.get(_type).getRelativeName().replace('.', '_');
try {
return TypeSystem.getByFullName("gw.vark.enums." + enumName);
}
catch (Exception e) {
AntlibTypeLoader.log("could not find generated enum type for " + enumName + " - must use EnumeratedAttribute instance instead", Project.MSG_VERBOSE);
}
_typeCategory = TypeCategory.PLAIN;
case PLAIN:
default:
return TypeSystem.get(clazz);
}
}
}
private static class TaskAdder extends TaskMethod {
super(helperKey, type);
}
@Override
return _helperKey + "List";
}
@Override
return new ParameterInfoBuilder()
.withName(getParamName())
.withType(makeListType(_type))
.withDefValue(GosuShop.getNullExpressionInstance());
}
@Override
void invoke(Task taskInstance, Object arg, IntrospectionHelper helper) {
for (Object argListArg : (List) arg) {
try {
helper.getElementMethod(_helperKey).invoke(taskInstance, argListArg);
} catch (IllegalAccessException e) {
throw GosuExceptionUtil.forceThrow(e);
} catch (InvocationTargetException e) {
throw GosuExceptionUtil.forceThrow(e);
}
}
}
}
super(helperKey, type);
}
@Override
return _helperKey + "Blocks";
}
@Override
return new ParameterInfoBuilder()
.withName(getParamName())
.withType(makeListOfBlocksType(_type))
.withDefValue(GosuShop.getNullExpressionInstance());
}
@Override
void invoke(Task taskInstance, Object arg, IntrospectionHelper helper) {
for (Object argListArg : (List) arg) {
Object created = helper.getElementCreator(null, "", taskInstance, _helperKey, null).create();
IFunction1 f = (IFunction1) argListArg;
f.invoke(created);
}
}
}
private final String _paramName;
private final Method _method;
super(null, type);
_paramName = paramName;
_method = method;
}
@Override
return _paramName;
}
@Override
return new ParameterInfoBuilder()
.withName(getParamName())
.withType(makeListType(_type))
.withDefValue(GosuShop.getNullExpressionInstance());
}
@Override
void invoke(Task taskInstance, Object arg, IntrospectionHelper helper) {
for (Object argListArg : (List) arg) {
try {
_method.invoke(taskInstance, argListArg);
} catch (IllegalAccessException e) {
throw GosuExceptionUtil.forceThrow(e);
} catch (InvocationTargetException e) {
throw GosuExceptionUtil.forceThrow(e);
}
}
}
}
private String _taskName;
private final Class<? extends Task> _taskClass;
private final TaskMethods _taskMethods;
public TaskMethodCallHandler(String taskName, Class<?
extends Task> taskClass, TaskMethods taskMethods) {
_taskName = taskName;
_taskClass = taskClass;
_taskMethods = taskMethods;
}
@Override
public Object
handleCall(Object ctx, Object... args) {
try {
IntrospectionHelper helper = IntrospectionHelper.getHelper(_taskClass);
Task taskInstance = _taskClass.newInstance();
taskInstance.setProject(Aardvark.getProject());
taskInstance.setTaskName(_taskName);
taskInstance.init();
ArrayList<TaskMethod> taskMethods = _taskMethods.asList();
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
taskMethods.get(i).invoke(taskInstance, args[i], helper);
}
}
taskInstance.execute();
return taskInstance;
} catch (IllegalAccessException e) {
throw GosuExceptionUtil.forceThrow(e);
} catch (InstantiationException e) {
throw GosuExceptionUtil.forceThrow(e);
}
}
}
}