blob: b31a8a2f16d301269adb8b4e44c3a3db4d77ed7d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.commons.weaver;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.weaver.model.ScanRequest;
import org.apache.commons.weaver.model.ScanResult;
import org.apache.commons.weaver.model.Scanner;
import org.apache.commons.weaver.model.WeaveInterest;
import org.apache.commons.weaver.utils.Annotations;
import org.apache.xbean.asm5.AnnotationVisitor;
import org.apache.xbean.asm5.ClassReader;
import org.apache.xbean.asm5.ClassVisitor;
import org.apache.xbean.asm5.FieldVisitor;
import org.apache.xbean.asm5.MethodVisitor;
import org.apache.xbean.asm5.Opcodes;
import org.apache.xbean.asm5.Type;
import org.apache.xbean.finder.Annotated;
import org.apache.xbean.finder.AnnotationFinder;
import org.apache.xbean.finder.Parameter;
import org.apache.xbean.finder.archive.Archive;
/**
* Scanner implementation.
*/
class Finder extends AnnotationFinder implements Scanner {
private abstract class AnnotationInflater extends AnnotationCapturer {
final Class<? extends Annotation> annotationType;
final Map<String, Object> elements = new LinkedHashMap<String, Object>();
AnnotationInflater(final String desc, final AnnotationVisitor wrapped) {
super(wrapped);
this.annotationType = toClass(Type.getType(desc)).asSubclass(Annotation.class);
}
Annotation inflate() {
return Annotations.instanceOf(annotationType, elements);
}
@Override
protected void storeValue(final String name, final Object value) {
Object toStore = value;
Validate.notNull(toStore, "null annotation element");
if (toStore.getClass().isArray()) {
final Class<?> requiredType;
try {
requiredType = annotationType.getDeclaredMethod(name).getReturnType();
} catch (final Exception e) {
throw new RuntimeException(e);
}
if (!requiredType.isInstance(toStore)) {
final int len = Array.getLength(toStore);
final Object typedArray = Array.newInstance(requiredType.getComponentType(), len);
for (int i = 0; i < len; i++) {
Object element = Array.get(toStore, i);
if (element instanceof Type) {
element = toClass((Type) element);
}
Array.set(typedArray, i, element);
}
toStore = typedArray;
}
} else if (toStore instanceof Type) {
toStore = toClass((Type) toStore);
}
elements.put(name, toStore);
}
}
private abstract class AnnotationCapturer extends AnnotationVisitor {
public AnnotationCapturer(final AnnotationVisitor wrapped) {
super(Opcodes.ASM5, wrapped);
}
/**
* Template method for storing an annotation value.
* @param name
* @param value
*/
protected abstract void storeValue(String name, Object value);
@Override
public void visit(final String name, final Object value) {
storeValue(name, value);
}
@Override
public AnnotationVisitor visitAnnotation(final String name, final String desc) {
final AnnotationCapturer owner = this;
return new AnnotationInflater(desc, super.visitAnnotation(name, desc)) {
@Override
public void visitEnd() {
owner.storeValue(name, inflate());
}
};
}
@Override
public AnnotationVisitor visitArray(final String name) {
final AnnotationCapturer owner = this;
final List<Object> values = new ArrayList<Object>();
return new AnnotationCapturer(super.visitArray(name)) {
@Override
public void visitEnd() {
owner.storeValue(name, values.toArray());
super.visitEnd();
}
@Override
protected void storeValue(final String name, final Object value) {
values.add(value);
}
};
}
@Override
public void visitEnum(final String name, final String desc, final String value) {
super.visitEnum(name, desc, value);
@SuppressWarnings("rawtypes")
final Class<? extends Enum> enumType;
try {
enumType = Class.forName(Type.getType(desc).getClassName()).asSubclass(Enum.class);
} catch (final ClassNotFoundException e) {
throw new RuntimeException(e);
}
@SuppressWarnings("unchecked")
final Enum<?> enumValue = Enum.valueOf(enumType, value);
storeValue(name, enumValue);
}
}
private class TopLevelAnnotationInflater extends AnnotationInflater {
private final Info info;
TopLevelAnnotationInflater(final String desc, final AnnotationVisitor wrapped, final Info info) {
super(desc, wrapped);
this.info = info;
}
@Override
public void visitEnd() {
super.visitEnd();
classfileAnnotationsFor(info).add(inflate());
}
private List<Annotation> classfileAnnotationsFor(final Info info) {
synchronized (CLASSFILE_ANNOTATIONS) {
if (!CLASSFILE_ANNOTATIONS.get().containsKey(info)) {
final List<Annotation> result = new ArrayList<Annotation>();
CLASSFILE_ANNOTATIONS.get().put(info, result);
return result;
}
}
return CLASSFILE_ANNOTATIONS.get().get(info);
}
}
/**
* Specialized {@link ClassVisitor} to inflate annotations for the info
* objects built by a wrapped {@link InfoBuildingVisitor}.
*/
public class Visitor extends ClassVisitor {
private final InfoBuildingVisitor wrapped;
public Visitor(final InfoBuildingVisitor wrapped) {
super(Opcodes.ASM5, wrapped);
this.wrapped = wrapped;
}
@Override
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature,
final Object value) {
final FieldVisitor toWrap = wrapped.visitField(access, name, desc, signature, value);
final ClassInfo classInfo = (ClassInfo) wrapped.getInfo();
final Type fieldType = Type.getType(desc);
FieldInfo testFieldInfo = null;
// should be the most recently added field, so iterate backward:
for (int i = classInfo.getFields().size() - 1; i >= 0; i--) {
final FieldInfo atI = classInfo.getFields().get(i);
if (atI.getName().equals(name)) {
final String type = atI.getType();
if (StringUtils.equals(type, fieldType.getClassName())
|| StringUtils.equals(type, fieldType.getDescriptor())) {
testFieldInfo = atI;
break;
}
}
}
if (testFieldInfo == null) {
return toWrap;
}
final FieldInfo fieldInfo = testFieldInfo;
return new FieldVisitor(Opcodes.ASM5, toWrap) {
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
final AnnotationVisitor toWrap = super.visitAnnotation(desc, visible);
return visible ? toWrap : new TopLevelAnnotationInflater(desc, toWrap, fieldInfo);
}
};
}
@Override
@SuppressWarnings("PMD.UseVarargs") // overridden method
public MethodVisitor visitMethod(final int access, final String name, final String desc,
final String signature, final String[] exceptions) {
final MethodVisitor toWrap = wrapped.visitMethod(access, name, desc, signature, exceptions);
final ClassInfo classInfo = (ClassInfo) wrapped.getInfo();
// MethodInfo may not always come from a descriptor, so we must go by the
// Member represented. Make sure the method either has a valid name or is a constructor:
final MethodInfo compareMethodInfo = new MethodInfo(classInfo, name, desc);
if (!compareMethodInfo.isConstructor() && !isJavaIdentifier(name)) {
return toWrap;
}
MethodInfo testMethodInfo = null;
// should be the most recently added method, so iterate backward:
for (int i = classInfo.getMethods().size() - 1; i >= 0; i--) {
final MethodInfo atI = classInfo.getMethods().get(i);
if (atI.getName().equals(name) && StringUtils.equals(atI.getDescriptor(), desc)) {
testMethodInfo = atI;
break;
}
}
if (testMethodInfo == null) {
return toWrap;
}
final MethodInfo methodInfo = testMethodInfo;
return new MethodVisitor(Opcodes.ASM5, toWrap) {
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
final AnnotationVisitor toWrap = super.visitAnnotation(desc, visible);
return visible ? toWrap : new TopLevelAnnotationInflater(desc, toWrap, methodInfo);
}
@Override
public AnnotationVisitor visitParameterAnnotation(final int param, final String desc,
final boolean visible) {
final AnnotationVisitor toWrap = super.visitParameterAnnotation(param, desc, visible);
if (visible) {
return toWrap;
}
ParameterInfo parameterInfo = null;
// should be the most recently added parameter, so iterate backward:
for (int i = methodInfo.getParameters().size() - 1; i >= 0; i--) {
final ParameterInfo atI = methodInfo.getParameters().get(i);
if (atI.getName().equals(Integer.toString(param))) {
parameterInfo = atI;
break;
}
}
return parameterInfo == null ? toWrap : new TopLevelAnnotationInflater(desc, toWrap, parameterInfo);
}
};
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
final AnnotationVisitor toWrap = super.visitAnnotation(desc, visible);
return visible ? toWrap : new TopLevelAnnotationInflater(desc, toWrap, wrapped.getInfo());
}
private boolean isJavaIdentifier(final String toCheck) {
if (toCheck.isEmpty() || !Character.isJavaIdentifierStart(toCheck.charAt(0))) {
return false;
}
for (final char chr : toCheck.substring(1).toCharArray()) {
if (!Character.isJavaIdentifierPart(chr)) {
return false;
}
}
return true;
}
}
private static class IncludesClassfile<T extends AnnotatedElement> implements Annotated<T> {
private final T target;
private final Annotation[] annotations;
IncludesClassfile(final T target, final List<Annotation> classfileAnnotations) {
this(target, classfileAnnotations.toArray(new Annotation[classfileAnnotations.size()]));
}
@SuppressWarnings("PMD.UseVarargs") // varargs not necessary here
IncludesClassfile(final T target, final Annotation[] classfileAnnotations) {
super();
this.target = target;
this.annotations = ArrayUtils.addAll(target.getAnnotations(), classfileAnnotations);
}
@Override
public <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
for (final Annotation prospect : annotations) {
if (prospect.annotationType().equals(annotationType)) {
@SuppressWarnings("unchecked")
final A result = (A) prospect;
return result;
}
}
return null;
}
@Override
public Annotation[] getAnnotations() {
final Annotation[] result = new Annotation[annotations.length];
System.arraycopy(annotations, 0, result, 0, annotations.length);
return result;
}
@Override
public Annotation[] getDeclaredAnnotations() {
return getAnnotations();
}
@Override
public boolean isAnnotationPresent(final Class<? extends Annotation> annotationType) {
return getAnnotation(annotationType) != null;
}
@Override
public T get() {
return target;
}
}
/**
* Helper class for finding elements with annotations (including those with classfile-level retention).
*/
public final class WithAnnotations {
private static final String INIT = "<init>";
private WithAnnotations() {
}
public List<Annotated<Package>> findAnnotatedPackages(final Class<? extends Annotation> annotation) {
Finder.this.findAnnotatedPackages(annotation);
final List<Annotated<Package>> result = new ArrayList<Annotated<Package>>();
for (final Info info : getAnnotationInfos(annotation.getName())) {
if (info instanceof PackageInfo) {
final PackageInfo packageInfo = (PackageInfo) info;
try {
final IncludesClassfile<Package> annotated =
new IncludesClassfile<Package>(packageInfo.get(), classfileAnnotationsFor(packageInfo));
if (annotated.isAnnotationPresent(annotation)) {
result.add(annotated);
}
} catch (final ClassNotFoundException e) {
continue;
}
}
}
return result;
}
/**
* Get the list of objects representing all scanned classes.
* @since 1.3
* @return {@link List} of {@link Annotated}{@code <Class<?>>}
*/
public List<Annotated<Class<?>>> getAllClasses() {
return annotate(originalInfos.values());
}
public List<Annotated<Class<?>>> findAnnotatedClasses(final Class<? extends Annotation> annotation) {
Finder.this.findAnnotatedClasses(annotation);
final List<Annotated<Class<?>>> annotatedClasses = annotate(getAnnotationInfos(annotation.getName()));
CollectionUtils.filter(annotatedClasses, new Predicate<Annotated<Class<?>>>() {
@Override
public boolean evaluate(Annotated<Class<?>> annotatedClass) {
return annotatedClass.isAnnotationPresent(annotation);
}
});
return annotatedClasses;
}
private List<Annotated<Class<?>>> annotate(Collection<? extends Info> infos) {
final List<Annotated<Class<?>>> result = new ArrayList<Annotated<Class<?>>>();
for (final Info info : infos) {
if (info instanceof ClassInfo) {
final ClassInfo classInfo = (ClassInfo) info;
try {
result.add(new IncludesClassfile<Class<?>>(classInfo.get(), classfileAnnotationsFor(classInfo)));
} catch (final ClassNotFoundException e) {
continue;
}
}
}
return result;
}
public List<Annotated<Class<?>>> findAssignableTypes(final Class<?> supertype) {
final List<Annotated<Class<?>>> result = new ArrayList<Annotated<Class<?>>>();
final List<?> assignableTypes;
if (supertype.isInterface()) {
assignableTypes = Finder.this.findImplementations(supertype);
} else {
assignableTypes = Finder.this.findSubclasses(supertype);
}
for (final Object object : assignableTypes) {
final ClassInfo classInfo = classInfos.get(((Class<?>) object).getName());
final IncludesClassfile<Class<?>> annotated;
try {
annotated = new IncludesClassfile<Class<?>>(classInfo.get(), classfileAnnotationsFor(classInfo));
} catch (final ClassNotFoundException e) {
continue;
}
result.add(annotated);
}
return result;
}
public List<Annotated<Method>> findAnnotatedMethods(final Class<? extends Annotation> annotation) {
Finder.this.findAnnotatedMethods(annotation);
final List<Annotated<Method>> result = new ArrayList<Annotated<Method>>();
for (final Info info : getAnnotationInfos(annotation.getName())) {
if (info instanceof MethodInfo) {
final MethodInfo methodInfo = (MethodInfo) info;
if (INIT.equals(methodInfo.getName())) {
continue;
}
IncludesClassfile<Method> annotated;
try {
annotated =
new IncludesClassfile<Method>((Method) methodInfo.get(),
classfileAnnotationsFor(methodInfo));
} catch (final ClassNotFoundException e) {
continue;
}
if (annotated.isAnnotationPresent(annotation)) {
result.add(annotated);
}
}
}
return result;
}
public List<Annotated<Parameter<Method>>> findAnnotatedMethodParameters(
final Class<? extends Annotation> annotationType) {
Finder.this.findAnnotatedMethodParameters(annotationType);
final List<Annotated<Parameter<Method>>> result = new ArrayList<Annotated<Parameter<Method>>>();
for (final Info info : getAnnotationInfos(annotationType.getName())) {
if (info instanceof ParameterInfo) {
final ParameterInfo parameterInfo = (ParameterInfo) info;
if (INIT.equals(parameterInfo.getDeclaringMethod().getName())) {
continue;
}
Parameter<Method> parameter;
try {
@SuppressWarnings("unchecked")
final Parameter<Method> unchecked = (Parameter<Method>) parameterInfo.get();
parameter = unchecked;
} catch (final ClassNotFoundException e) {
continue;
}
final IncludesClassfile<Parameter<Method>> annotated =
new IncludesClassfile<Parameter<Method>>(parameter, classfileAnnotationsFor(parameterInfo));
if (annotated.isAnnotationPresent(annotationType)) {
result.add(annotated);
}
}
}
return result;
}
public List<Annotated<Constructor<?>>> findAnnotatedConstructors(final Class<? extends Annotation> annotation) {
Finder.this.findAnnotatedConstructors(annotation);
final List<Annotated<Constructor<?>>> result = new ArrayList<Annotated<Constructor<?>>>();
for (final Info info : getAnnotationInfos(annotation.getName())) {
if (info instanceof MethodInfo) {
final MethodInfo methodInfo = (MethodInfo) info;
if (!INIT.equals(methodInfo.getName())) {
continue;
}
final IncludesClassfile<Constructor<?>> annotated;
try {
annotated =
new IncludesClassfile<Constructor<?>>((Constructor<?>) methodInfo.get(),
classfileAnnotationsFor(methodInfo));
} catch (final ClassNotFoundException e) {
continue;
}
if (annotated.isAnnotationPresent(annotation)) {
result.add(annotated);
}
}
}
return result;
}
public List<Annotated<Parameter<Constructor<?>>>> findAnnotatedConstructorParameters(
final Class<? extends Annotation> annotation) {
Finder.this.findAnnotatedConstructorParameters(annotation);
final List<Annotated<Parameter<Constructor<?>>>> result =
new ArrayList<Annotated<Parameter<Constructor<?>>>>();
for (final Info info : getAnnotationInfos(annotation.getName())) {
if (info instanceof ParameterInfo) {
final ParameterInfo parameterInfo = (ParameterInfo) info;
if (!INIT.equals(parameterInfo.getDeclaringMethod().getName())) {
continue;
}
Parameter<Constructor<?>> parameter;
try {
@SuppressWarnings("unchecked")
final Parameter<Constructor<?>> unchecked = (Parameter<Constructor<?>>) parameterInfo.get();
parameter = unchecked;
} catch (final ClassNotFoundException e) {
continue;
}
final IncludesClassfile<Parameter<Constructor<?>>> annotated =
new IncludesClassfile<Parameter<Constructor<?>>>(parameter,
classfileAnnotationsFor(parameterInfo));
if (annotated.isAnnotationPresent(annotation)) {
result.add(annotated);
}
}
}
return result;
}
public List<Annotated<Field>> findAnnotatedFields(final Class<? extends Annotation> annotation) {
Finder.this.findAnnotatedFields(annotation);
final List<Annotated<Field>> result = new ArrayList<Annotated<Field>>();
for (final Info info : getAnnotationInfos(annotation.getName())) {
if (info instanceof FieldInfo) {
final FieldInfo fieldInfo = (FieldInfo) info;
try {
final IncludesClassfile<Field> annotated =
new IncludesClassfile<Field>((Field) fieldInfo.get(), classfileAnnotationsFor(fieldInfo));
if (annotated.isAnnotationPresent(annotation)) {
result.add(annotated);
}
} catch (final ClassNotFoundException e) {
continue;
}
}
}
return result;
}
private List<Annotation> classfileAnnotationsFor(final Info info) {
synchronized (classfileAnnotations) {
if (!classfileAnnotations.containsKey(info)) {
final List<Annotation> result = new ArrayList<Annotation>();
classfileAnnotations.put(info, result);
return result;
}
}
return classfileAnnotations.get(info);
}
}
private static final int ASM_FLAGS = ClassReader.SKIP_CODE + ClassReader.SKIP_DEBUG + ClassReader.SKIP_FRAMES;
/**
* The {@link #classfileAnnotations} member stores these; however the scanning takes place in the scope of the super
* constructor call, thus there is no opportunity to set the reference beforehand. To work around this, we use a
* static ThreadLocal with an initializer and pull/clear its value when we return from the super constructor. :P
*/
private static final ThreadLocal<Map<Info, List<Annotation>>> CLASSFILE_ANNOTATIONS =
new ThreadLocal<Map<Info, List<Annotation>>>() {
@Override
protected Map<Info, List<Annotation>> initialValue() {
return new IdentityHashMap<AnnotationFinder.Info, List<Annotation>>();
}
};
private final WithAnnotations withAnnotations = new WithAnnotations();
private final Map<Info, List<Annotation>> classfileAnnotations;
private final Inflater inflater;
/**
* Create a new {@link Finder} instance.
* @param archive
*/
public Finder(final Archive archive) {
super(archive, false);
classfileAnnotations = CLASSFILE_ANNOTATIONS.get();
CLASSFILE_ANNOTATIONS.remove();
inflater = new Inflater(classfileAnnotations);
enableFindImplementations();
enableFindSubclasses();
}
/**
* Fluent "finder with annotations".
* @return {@link WithAnnotations}
*/
public WithAnnotations withAnnotations() {
return withAnnotations;
}
/**
* {@inheritDoc}
*/
@Override
protected void readClassDef(final InputStream bytecode) throws IOException {
try {
final ClassReader classReader = new ClassReader(bytecode);
classReader.accept(new Visitor(new InfoBuildingVisitor()), ASM_FLAGS);
} finally {
bytecode.close();
}
}
/**
* {@inheritDoc}
*/
@Override
public AnnotationFinder select(final Class<?>... arg0) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public AnnotationFinder select(final Iterable<String> clazz) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public AnnotationFinder select(final String... clazz) {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@Override
public ScanResult scan(final ScanRequest request) {
final ScanResult result = new ScanResult();
if (!request.isConstrained() || request.getSupertypes().contains(Object.class)) {
for (final Annotated<Class<?>> type : this.withAnnotations().getAllClasses()) {
result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
}
} else {
for (final WeaveInterest interest : request.getInterests()) {
switch (interest.target) {
case PACKAGE:
for (final Annotated<Package> pkg : this.withAnnotations().findAnnotatedPackages(
interest.annotationType)) {
result.getWeavable(pkg.get()).addAnnotations(pkg.getAnnotations());
}
break;
case TYPE:
for (final Annotated<Class<?>> type : this.withAnnotations().findAnnotatedClasses(
interest.annotationType)) {
result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
}
break;
case METHOD:
for (final Annotated<Method> method : this.withAnnotations().findAnnotatedMethods(
interest.annotationType)) {
result.getWeavable(method.get()).addAnnotations(method.getAnnotations());
}
break;
case CONSTRUCTOR:
for (final Annotated<Constructor<?>> ctor : this.withAnnotations().findAnnotatedConstructors(
interest.annotationType)) {
result.getWeavable(ctor.get()).addAnnotations(ctor.getAnnotations());
}
break;
case FIELD:
for (final Annotated<Field> fld : this.withAnnotations().findAnnotatedFields(interest.annotationType)) {
result.getWeavable(fld.get()).addAnnotations(fld.getAnnotations());
}
break;
case PARAMETER:
for (final Annotated<Parameter<Method>> parameter : this.withAnnotations()
.findAnnotatedMethodParameters(interest.annotationType)) {
result.getWeavable(parameter.get().getDeclaringExecutable())
.getWeavableParameter(parameter.get().getIndex()).addAnnotations(parameter.getAnnotations());
}
for (final Annotated<Parameter<Constructor<?>>> parameter : this.withAnnotations()
.findAnnotatedConstructorParameters(interest.annotationType)) {
result.getWeavable(parameter.get().getDeclaringExecutable())
.getWeavableParameter(parameter.get().getIndex()).addAnnotations(parameter.getAnnotations());
}
break;
default:
// should we log something?
break;
}
}
for (final Class<?> supertype : request.getSupertypes()) {
for (final Annotated<Class<?>> type : this.withAnnotations().findAssignableTypes(supertype)) {
result.getWeavable(type.get()).addAnnotations(type.getAnnotations());
}
}
}
return inflater.inflate(result);
}
private Class<?> toClass(final Type type) {
final String className;
if (type.getSort() == Type.ARRAY) {
className = type.getElementType().getClassName();
} else {
className = type.getClassName();
}
Class<?> result;
try {
result = Class.forName(className);
} catch (final ClassNotFoundException e) {
try {
result = getArchive().loadClass(className);
} catch (final ClassNotFoundException e1) {
throw new RuntimeException(e1);
}
}
if (type.getSort() == Type.ARRAY) {
final int[] dims = new int[type.getDimensions()];
Arrays.fill(dims, 0);
result = Array.newInstance(result, dims).getClass();
}
return result;
}
}