diff --git a/java/src/org/broadinstitute/sting/queue/util/ClassType.java b/java/src/org/broadinstitute/sting/queue/util/ClassType.java index ae8bcdaef..50fbf58e7 100644 --- a/java/src/org/broadinstitute/sting/queue/util/ClassType.java +++ b/java/src/org/broadinstitute/sting/queue/util/ClassType.java @@ -27,8 +27,7 @@ package org.broadinstitute.sting.queue.util; import java.lang.annotation.*; /** - * Specifies the type of an input our output field. - * Retains it during runtime to work around type erasure. + * Specifies the type of an input or output field. * Written in java because scala doesn't support RetentionPolicy.RUNTIME */ @Documented diff --git a/scala/src/org/broadinstitute/sting/queue/function/MemoryLimitedFunction.scala b/scala/src/org/broadinstitute/sting/queue/function/MemoryLimitedFunction.scala index 7f9d98542..c7fad1500 100644 --- a/scala/src/org/broadinstitute/sting/queue/function/MemoryLimitedFunction.scala +++ b/scala/src/org/broadinstitute/sting/queue/function/MemoryLimitedFunction.scala @@ -1,10 +1,9 @@ package org.broadinstitute.sting.queue.function -import org.broadinstitute.sting.queue.util.{Input, Optional, ClassType} +import org.broadinstitute.sting.queue.util.{Input, Optional} trait MemoryLimitedFunction { @Input @Optional - @ClassType(classOf[Int]) var memoryLimit: Option[Int] = None } diff --git a/scala/src/org/broadinstitute/sting/queue/function/gatk/GatkFunction.scala b/scala/src/org/broadinstitute/sting/queue/function/gatk/GatkFunction.scala index 6d6fb7b8e..f7eeead32 100644 --- a/scala/src/org/broadinstitute/sting/queue/function/gatk/GatkFunction.scala +++ b/scala/src/org/broadinstitute/sting/queue/function/gatk/GatkFunction.scala @@ -1,7 +1,7 @@ package org.broadinstitute.sting.queue.function.gatk import java.io.File -import org.broadinstitute.sting.queue.util.{ClassType, Input, Optional} +import org.broadinstitute.sting.queue.util.{Input, Optional} import org.broadinstitute.sting.queue.function.{MemoryLimitedFunction, IntervalFunction, CommandLineFunction} trait GatkFunction extends CommandLineFunction with MemoryLimitedFunction with IntervalFunction { @@ -17,7 +17,6 @@ trait GatkFunction extends CommandLineFunction with MemoryLimitedFunction with I @Input @Optional - @ClassType(classOf[File]) var bamFiles: List[File] = Nil @Input diff --git a/scala/src/org/broadinstitute/sting/queue/util/ReflectionUtils.scala b/scala/src/org/broadinstitute/sting/queue/util/ReflectionUtils.scala index cfc10f409..ccd3aceaf 100644 --- a/scala/src/org/broadinstitute/sting/queue/util/ReflectionUtils.scala +++ b/scala/src/org/broadinstitute/sting/queue/util/ReflectionUtils.scala @@ -1,11 +1,11 @@ package org.broadinstitute.sting.queue.util -import java.lang.reflect.Field import org.broadinstitute.sting.queue.QException import java.lang.annotation.Annotation import scala.concurrent.JavaConversions import scala.concurrent.JavaConversions._ import scala.collection.immutable.ListMap +import java.lang.reflect.{ParameterizedType, Field} object ReflectionUtils { def getField(obj: AnyRef, name: String) = getAllFields(obj.getClass).find(_.getName == name) @@ -41,10 +41,7 @@ object ReflectionUtils { if (classOf[Seq[_]].isAssignableFrom(field.getType)) { - if (!field.isAnnotationPresent(classOf[ClassType])) - throw new QException("@ClassType must be specified due to type erasure for field: " + field) - - val fieldType = field.getAnnotation(classOf[ClassType]).asInstanceOf[ClassType].value + val fieldType = getCollectionType(field) val typeValue = coerce(fieldType, value) var list = getter.invoke(obj).asInstanceOf[Seq[_]] @@ -53,10 +50,7 @@ object ReflectionUtils { } else if (classOf[Option[_]].isAssignableFrom(field.getType)) { - if (!field.isAnnotationPresent(classOf[ClassType])) - throw new QException("@ClassType must be specified due to type erasure for field: " + field) - - val fieldType = field.getAnnotation(classOf[ClassType]).asInstanceOf[ClassType].value + val fieldType = getCollectionType(field) val typeValue = coerce(fieldType, value) setter.invoke(obj, Some(typeValue)) @@ -70,6 +64,29 @@ object ReflectionUtils { } } + private def getCollectionType(field: Field) = { + getGenericTypes(field) match { + case Some(classes) => + if (classes.length > 1) + throw new IllegalArgumentException("Field contains more than one generic type: " + field) + classes(0) + case None => + if (!field.isAnnotationPresent(classOf[ClassType])) + throw new QException("@ClassType must be specified for unparameterized field: " + field) + field.getAnnotation(classOf[ClassType]).asInstanceOf[ClassType].value + } + } + + private def getGenericTypes(field: Field) = { + // TODO: Refactor: based on java code in org.broadinstitute.sting.commandline.ArgumentTypeDescriptor + // If this is a parameterized collection, find the contained type. If blow up if only one type exists. + if (field.getGenericType.isInstanceOf[ParameterizedType]) { + val parameterizedType = field.getGenericType.asInstanceOf[ParameterizedType] + Some(parameterizedType.getActualTypeArguments.map(_.asInstanceOf[Class[_]])) + } + else None + } + private[util] def fieldGetter(field: Field) = field.getDeclaringClass.getMethod(field.getName) private[util] def fieldSetter(field: Field) = field.getDeclaringClass.getMethod(field.getName+"_$eq", field.getType)