/*
* Copyright 2017 TNG Technology Consulting GmbH
*
* 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 com.tngtech.archunit.core.importer;
import java.lang.reflect.Array;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.Chars;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
import com.tngtech.archunit.Internal;
import com.tngtech.archunit.base.Function;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.MayResolveTypesViaReflection;
import com.tngtech.archunit.core.domain.JavaAnnotation;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaEnumConstant;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.JavaModifier;
import com.tngtech.archunit.core.domain.JavaType;
import com.tngtech.archunit.core.importer.RawAccessRecord.CodeUnit;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.tngtech.archunit.core.domain.DomainObjectCreationContext.createSource;
import static com.tngtech.archunit.core.domain.JavaConstructor.CONSTRUCTOR_NAME;
import static com.tngtech.archunit.core.domain.JavaStaticInitializer.STATIC_INITIALIZER_NAME;
import static com.tngtech.archunit.core.importer.ClassFileProcessor.ASM_API_VERSION;
class JavaClassProcessor extends ClassVisitor {
private static final Logger LOG = LoggerFactory.getLogger(JavaClassProcessor.class);
private static final AccessHandler NO_OP = new AccessHandler.NoOp();
private DomainBuilders.JavaClassBuilder javaClassBuilder;
private final Set<DomainBuilders.JavaAnnotationBuilder> annotations = new HashSet<>();
private final URI sourceURI;
private final DeclarationHandler declarationHandler;
private final AccessHandler accessHandler;
private String className;
JavaClassProcessor(URI sourceURI, DeclarationHandler declarationHandler) {
this(sourceURI, declarationHandler, NO_OP);
}
JavaClassProcessor(URI sourceURI, DeclarationHandler declarationHandler, AccessHandler accessHandler) {
super(ASM_API_VERSION);
this.sourceURI = sourceURI;
this.declarationHandler = declarationHandler;
this.accessHandler = accessHandler;
}
Optional<JavaClass> createJavaClass() {
return javaClassBuilder != null ? Optional.of(javaClassBuilder.build()) : Optional.<JavaClass>absent();
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
LOG.info("Analysing class '{}'", name);
JavaType javaType = JavaTypeImporter.createFromAsmObjectTypeName(name);
if (alreadyImported(javaType)) {
return;
}
ImmutableSet<String> interfaceNames = createInterfaceNames(interfaces);
LOG.debug("Found interfaces {} on class '{}'", interfaceNames, name);
boolean opCodeForInterfaceIsPresent = (access & Opcodes.ACC_INTERFACE) != 0;
boolean opCodeForEnumIsPresent = (access & Opcodes.ACC_ENUM) != 0;
Optional<String> superClassName = getSuperClassName(superName, opCodeForInterfaceIsPresent);
LOG.debug("Found superclass {} on class '{}'", superClassName, name);
javaClassBuilder = new DomainBuilders.JavaClassBuilder()
.withSource(createSource(sourceURI))
.withType(javaType)
.withInterface(opCodeForInterfaceIsPresent)
.withEnum(opCodeForEnumIsPresent)
.withModifiers(JavaModifier.getModifiersForClass(access));
className = javaType.getName();
declarationHandler.onNewClass(className, superClassName, interfaceNames);
}
private boolean alreadyImported(JavaType javaType) {
return !declarationHandler.isNew(javaType.getName());
}
// NOTE: For some reason ASM claims superName == java/lang/Object for Interfaces???
// This is inconsistent with the behavior of Class.getSuperClass()
private Optional<String> getSuperClassName(String superName, boolean isInterface) {
return superName != null && !isInterface ?
Optional.of(createTypeName(superName)) :
Optional.<String>absent();
}
private boolean importAborted() {
return javaClassBuilder == null;
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
if (importAborted()) {
return;
}
if (name != null && outerName != null) {
String innerTypeName = createTypeName(name);
correctModifiersForNestedClass(innerTypeName, access);
declarationHandler.registerEnclosingClass(innerTypeName, createTypeName(outerName));
}
}
// Modifier handling is somewhat counter intuitive for nested classes, even though we 'visit' the nested class
// like any outer class in visit(..) before, the modifiers like 'PUBLIC' or 'PRIVATE'
// are found in the access flags of visitInnerClass(..)
private void correctModifiersForNestedClass(String innerTypeName, int access) {
if (innerTypeName.equals(className)) {
javaClassBuilder.withModifiers(JavaModifier.getModifiersForClass(access));
}
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
if (importAborted()) {
return;
}
declarationHandler.registerEnclosingClass(className, createTypeName(owner));
}
private ImmutableSet<String> createInterfaceNames(String[] interfaces) {
ImmutableSet.Builder<String> result = ImmutableSet.builder();
for (String i : interfaces) {
result.add(createTypeName(i));
}
return result.build();
}
private String createTypeName(String name) {
return name.replace("/", ".");
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (importAborted()) {
return super.visitField(access, name, desc, signature, value);
}
DomainBuilders.JavaFieldBuilder fieldBuilder = new DomainBuilders.JavaFieldBuilder()
.withName(name)
.withType(JavaTypeImporter.importAsmType(Type.getType(desc)))
.withModifiers(JavaModifier.getModifiersForField(access))
.withDescriptor(desc);
declarationHandler.onDeclaredField(fieldBuilder);
return new FieldProcessor(fieldBuilder);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (importAborted()) {
return super.visitMethod(access, name, desc, signature, exceptions);
}
LOG.debug("Analysing method {}.{}:{}", className, name, desc);
accessHandler.setContext(new CodeUnit(name, namesOf(Type.getArgumentTypes(desc)), className));
DomainBuilders.JavaCodeUnitBuilder<?, ?> codeUnitBuilder = addCodeUnitBuilder(name);
Type methodType = Type.getMethodType(desc);
codeUnitBuilder
.withName(name)
.withModifiers(JavaModifier.getModifiersForMethod(access))
.withParameters(typesFrom(methodType.getArgumentTypes()))
.withReturnType(JavaTypeImporter.importAsmType(methodType.getReturnType()))
.withDescriptor(desc);
return new MethodProcessor(className, accessHandler, codeUnitBuilder);
}
private List<JavaType> typesFrom(Type[] asmTypes) {
List<JavaType> result = new ArrayList<>();
for (Type asmType : asmTypes) {
result.add(JavaTypeImporter.importAsmType(asmType));
}
return result;
}
private DomainBuilders.JavaCodeUnitBuilder<?, ?> addCodeUnitBuilder(String name) {
if (CONSTRUCTOR_NAME.equals(name)) {
DomainBuilders.JavaConstructorBuilder builder = new DomainBuilders.JavaConstructorBuilder();
declarationHandler.onDeclaredConstructor(builder);
return builder;
} else if (STATIC_INITIALIZER_NAME.equals(name)) {
DomainBuilders.JavaStaticInitializerBuilder builder = new DomainBuilders.JavaStaticInitializerBuilder();
declarationHandler.onDeclaredStaticInitializer(builder);
return builder;
} else {
DomainBuilders.JavaMethodBuilder builder = new DomainBuilders.JavaMethodBuilder();
declarationHandler.onDeclaredMethod(builder);
return builder;
}
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (importAborted()) {
return super.visitAnnotation(desc, visible);
}
return new AnnotationProcessor(addAnnotationTo(annotations), annotationBuilderFor(desc));
}
@Override
public void visitEnd() {
if (importAborted()) {
return;
}
declarationHandler.onDeclaredAnnotations(annotations);
LOG.debug("Done analysing {}", className);
}
private static List<String> namesOf(Type[] types) {
ImmutableList.Builder<String> result = ImmutableList.builder();
for (Type type : types) {
result.add(JavaTypeImporter.importAsmType(type).getName());
}
return result.build();
}
private static class MethodProcessor extends MethodVisitor {
private final String declaringClassName;
private final AccessHandler accessHandler;
private final DomainBuilders.JavaCodeUnitBuilder<?, ?> codeUnitBuilder;
private final Set<DomainBuilders.JavaAnnotationBuilder> annotations = new HashSet<>();
private int actualLineNumber;
MethodProcessor(String declaringClassName, AccessHandler accessHandler, DomainBuilders.JavaCodeUnitBuilder<?, ?> codeUnitBuilder) {
super(ASM_API_VERSION);
this.declaringClassName = declaringClassName;
this.accessHandler = accessHandler;
this.codeUnitBuilder = codeUnitBuilder;
}
@Override
public void visitCode() {
actualLineNumber = 0;
}
// NOTE: ASM doesn't reliably visit this method, so if this method is skipped, line number 0 is recorded
@Override
public void visitLineNumber(int line, Label start) {
LOG.debug("Examining line number {}", line);
actualLineNumber = line;
accessHandler.setLineNumber(actualLineNumber);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
accessHandler.handleFieldInstruction(opcode, owner, name, desc);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
accessHandler.handleMethodInstruction(owner, name, desc);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return new AnnotationProcessor(addAnnotationTo(annotations), annotationBuilderFor(desc));
}
@Override
public AnnotationVisitor visitAnnotationDefault() {
return new AnnotationDefaultProcessor(declaringClassName, codeUnitBuilder);
}
@Override
public void visitEnd() {
codeUnitBuilder.withAnnotations(annotations);
}
private static class AnnotationDefaultProcessor extends AnnotationVisitor {
private final String annotationTypeName;
private final DomainBuilders.JavaMethodBuilder methodBuilder;
AnnotationDefaultProcessor(String annotationTypeName, DomainBuilders.JavaCodeUnitBuilder<?, ?> codeUnitBuilder) {
super(ClassFileProcessor.ASM_API_VERSION);
this.annotationTypeName = annotationTypeName;
checkArgument(codeUnitBuilder instanceof DomainBuilders.JavaMethodBuilder,
"tried to import annotation defaults for code unit '%s' that is not a method " +
"(as any annotation.property() is assumed to be), " +
"this is likely a bug", codeUnitBuilder.getName());
methodBuilder = (DomainBuilders.JavaMethodBuilder) codeUnitBuilder;
}
@Override
public void visit(String name, Object value) {
methodBuilder.withAnnotationDefaultValue(AnnotationTypeConversion.convert(value));
}
@Override
public void visitEnum(String name, String desc, String value) {
methodBuilder.withAnnotationDefaultValue(javaEnumBuilder(desc, value));
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
return new AnnotationProcessor(new SetAsAnnotationDefault(annotationTypeName, methodBuilder), annotationBuilderFor(desc));
}
@Override
public AnnotationVisitor visitArray(String name) {
return new AnnotationArrayProcessor(new SetAsAnnotationDefault(annotationTypeName, methodBuilder));
}
}
}
private static class SetAsAnnotationDefault implements TakesAnnotationBuilder, AnnotationArrayContext {
private final String annotationTypeName;
private final DomainBuilders.JavaMethodBuilder methodBuilder;
private SetAsAnnotationDefault(String annotationTypeName, DomainBuilders.JavaMethodBuilder methodBuilder) {
this.annotationTypeName = annotationTypeName;
this.methodBuilder = methodBuilder;
}
@Override
public void add(DomainBuilders.JavaAnnotationBuilder annotation) {
setArrayResult(DomainBuilders.JavaAnnotationBuilder.ValueBuilder.from(annotation));
}
@Override
public String getDeclaringAnnotationTypeName() {
return annotationTypeName;
}
@Override
public String getDeclaringAnnotationMemberName() {
return methodBuilder.getName();
}
@Override
public void setArrayResult(DomainBuilders.JavaAnnotationBuilder.ValueBuilder valueBuilder) {
methodBuilder.withAnnotationDefaultValue(valueBuilder);
}
}
interface DeclarationHandler {
boolean isNew(String className);
void onNewClass(String className, Optional<String> superClassName, Set<String> interfaceNames);
void onDeclaredField(DomainBuilders.JavaFieldBuilder fieldBuilder);
void onDeclaredConstructor(DomainBuilders.JavaConstructorBuilder builder);
void onDeclaredMethod(DomainBuilders.JavaMethodBuilder builder);
void onDeclaredStaticInitializer(DomainBuilders.JavaStaticInitializerBuilder builder);
void onDeclaredAnnotations(Set<DomainBuilders.JavaAnnotationBuilder> annotations);
void registerEnclosingClass(String ownerName, String enclosingClassName);
}
interface AccessHandler {
void handleFieldInstruction(int opcode, String owner, String name, String desc);
void setContext(CodeUnit codeUnit);
void setLineNumber(int lineNumber);
void handleMethodInstruction(String owner, String name, String desc);
@Internal
class NoOp implements AccessHandler {
@Override
public void handleFieldInstruction(int opcode, String owner, String name, String desc) {
}
@Override
public void setContext(CodeUnit codeUnit) {
}
@Override
public void setLineNumber(int lineNumber) {
}
@Override
public void handleMethodInstruction(String owner, String name, String desc) {
}
}
}
private static class FieldProcessor extends FieldVisitor {
private final DomainBuilders.JavaFieldBuilder fieldBuilder;
private final Set<DomainBuilders.JavaAnnotationBuilder> annotations = new HashSet<>();
private FieldProcessor(DomainBuilders.JavaFieldBuilder fieldBuilder) {
super(ASM_API_VERSION);
this.fieldBuilder = fieldBuilder;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return new AnnotationProcessor(addAnnotationTo(annotations), annotationBuilderFor(desc));
}
@Override
public void visitEnd() {
fieldBuilder.withAnnotations(annotations);
}
}
private static DomainBuilders.JavaAnnotationBuilder annotationBuilderFor(String desc) {
return new DomainBuilders.JavaAnnotationBuilder().withType(JavaTypeImporter.importAsmType(Type.getType(desc)));
}
private static class AnnotationProcessor extends AnnotationVisitor {
private final TakesAnnotationBuilder takesAnnotationBuilder;
private final DomainBuilders.JavaAnnotationBuilder annotationBuilder;
private AnnotationProcessor(TakesAnnotationBuilder takesAnnotationBuilder, DomainBuilders.JavaAnnotationBuilder annotationBuilder) {
super(ASM_API_VERSION);
this.takesAnnotationBuilder = takesAnnotationBuilder;
this.annotationBuilder = annotationBuilder;
}
@Override
public void visit(String name, Object value) {
annotationBuilder.addProperty(name, AnnotationTypeConversion.convert(value));
}
@Override
public void visitEnum(String name, String desc, String value) {
annotationBuilder.addProperty(name, javaEnumBuilder(desc, value));
}
@Override
public AnnotationVisitor visitAnnotation(final String name, String desc) {
return new AnnotationProcessor(addAnnotationAsProperty(name, annotationBuilder), annotationBuilderFor(desc));
}
@Override
public AnnotationVisitor visitArray(final String name) {
return new AnnotationArrayProcessor(new AnnotationArrayContext() {
@Override
public String getDeclaringAnnotationTypeName() {
return annotationBuilder.getJavaType().getName();
}
@Override
public String getDeclaringAnnotationMemberName() {
return name;
}
@Override
public void setArrayResult(DomainBuilders.JavaAnnotationBuilder.ValueBuilder valueBuilder) {
annotationBuilder.addProperty(name, valueBuilder);
}
});
}
@Override
public void visitEnd() {
takesAnnotationBuilder.add(annotationBuilder);
}
}
private static TakesAnnotationBuilder addAnnotationTo(final Collection<? super DomainBuilders.JavaAnnotationBuilder> collection) {
return new TakesAnnotationBuilder() {
@Override
public void add(DomainBuilders.JavaAnnotationBuilder annotation) {
collection.add(annotation);
}
};
}
private static TakesAnnotationBuilder addAnnotationAsProperty(final String name, final DomainBuilders.JavaAnnotationBuilder annotationBuilder) {
return new TakesAnnotationBuilder() {
@Override
public void add(DomainBuilders.JavaAnnotationBuilder builder) {
annotationBuilder.addProperty(name, DomainBuilders.JavaAnnotationBuilder.ValueBuilder.from(builder));
}
};
}
private interface TakesAnnotationBuilder {
void add(DomainBuilders.JavaAnnotationBuilder annotation);
}
private static class AnnotationArrayProcessor extends AnnotationVisitor {
private final AnnotationArrayContext annotationArrayContext;
private Class<?> derivedComponentType;
private final List<DomainBuilders.JavaAnnotationBuilder.ValueBuilder> values = new ArrayList<>();
private AnnotationArrayProcessor(AnnotationArrayContext annotationArrayContext) {
super(ASM_API_VERSION);
this.annotationArrayContext = annotationArrayContext;
}
@Override
public void visit(String name, Object value) {
setDerivedComponentType(value);
values.add(AnnotationTypeConversion.convert(value));
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
setDerivedComponentType(JavaAnnotation.class);
return new AnnotationProcessor(new TakesAnnotationBuilder() {
@Override
public void add(DomainBuilders.JavaAnnotationBuilder annotationBuilder) {
values.add(DomainBuilders.JavaAnnotationBuilder.ValueBuilder.from(annotationBuilder));
}
}, annotationBuilderFor(desc));
}
@Override
public void visitEnum(String name, final String desc, final String value) {
setDerivedComponentType(JavaEnumConstant.class);
values.add(javaEnumBuilder(desc, value));
}
private void setDerivedComponentType(Object value) {
setDerivedComponentType(value.getClass());
}
// NOTE: If the declared annotation is not imported itself, we can still use this heuristic,
// to determine additional information about the respective array.
// (It the annotation is imported itself, we could easily determine this from the respective
// JavaClass methods)
private void setDerivedComponentType(Class<?> type) {
type = AnnotationTypeConversion.convert(type);
checkState(derivedComponentType == null || derivedComponentType.equals(type),
"Found mixed component types while importing array, this is most likely a bug");
derivedComponentType = type;
}
@Override
public void visitEnd() {
annotationArrayContext.setArrayResult(new ArrayValueBuilder());
}
private class ArrayValueBuilder extends DomainBuilders.JavaAnnotationBuilder.ValueBuilder {
@Override
public Optional<Object> build(ClassesByTypeName importedClasses) {
Optional<Class<?>> componentType = determineComponentType(importedClasses);
if (!componentType.isPresent()) {
return Optional.absent();
}
return Optional.of(toArray(componentType.get(), buildValues(importedClasses)));
}
@SuppressWarnings("unchecked") // NOTE: We assume the component type matches the list
private Object toArray(Class<?> componentType, List<Object> values) {
if (componentType == boolean.class) {
return Booleans.toArray((Collection) values);
} else if (componentType == byte.class) {
return Bytes.toArray((Collection) values);
} else if (componentType == short.class) {
return Shorts.toArray((Collection) values);
} else if (componentType == int.class) {
return Ints.toArray((Collection) values);
} else if (componentType == long.class) {
return Longs.toArray((Collection) values);
} else if (componentType == float.class) {
return Floats.toArray((Collection) values);
} else if (componentType == double.class) {
return Doubles.toArray((Collection) values);
} else if (componentType == char.class) {
return Chars.toArray((Collection) values);
}
return values.toArray((Object[]) Array.newInstance(componentType, values.size()));
}
private List<Object> buildValues(ClassesByTypeName importedClasses) {
List<Object> result = new ArrayList<>();
for (DomainBuilders.JavaAnnotationBuilder.ValueBuilder value : values) {
result.addAll(value.build(importedClasses).asSet());
}
return result;
}
private Optional<Class<?>> determineComponentType(ClassesByTypeName importedClasses) {
if (derivedComponentType != null) {
return Optional.<Class<?>>of(derivedComponentType);
}
JavaClass annotationType = importedClasses.get(annotationArrayContext.getDeclaringAnnotationTypeName());
Optional<JavaMethod> method = annotationType
.tryGetMethod(annotationArrayContext.getDeclaringAnnotationMemberName());
return method.isPresent() ?
determineComponentTypeFromReturnValue(method) :
Optional.<Class<?>>absent();
}
private Optional<Class<?>> determineComponentTypeFromReturnValue(Optional<JavaMethod> method) {
String name = method.get().getReturnType().getName();
Optional<Class<?>> result = AnnotationTypeConversion.tryConvert(name);
if (result.isPresent()) {
return Optional.<Class<?>>of(result.get().getComponentType());
}
return resolveComponentTypeFrom(name);
}
@MayResolveTypesViaReflection(reason = "Resolving primitives does not really use reflection")
private Optional<Class<?>> resolveComponentTypeFrom(String name) {
JavaType type = JavaType.From.name(name);
JavaType componentType = getComponentType(type);
if (componentType.isPrimitive()) {
return Optional.<Class<?>>of(componentType.resolveClass());
}
if (String.class.getName().equals(componentType.getName())) {
return Optional.<Class<?>>of(String.class);
}
// if we couldn't determine the type up to now, it must be an empty enum or annotation array,
// it's not completely consistent, but we'll just treat this as Object array for now and see
// if this will ever make a problem, since annotation proxy would to the conversion backwards
// and if one has to handle get(property): Object, this to be an empty Object[]
// instead of a JavaEnumConstant[] or JavaAnnotation[] should hardly cause any real problems
return Optional.<Class<?>>of(Object.class);
}
private JavaType getComponentType(JavaType type) {
Optional<JavaType> result = type.tryGetComponentType();
checkState(result.isPresent(), "Couldn't determine component type of array return type %s, " +
"this is most likely a bug", type.getName());
return result.get();
}
}
}
private interface AnnotationArrayContext {
String getDeclaringAnnotationTypeName();
String getDeclaringAnnotationMemberName();
void setArrayResult(DomainBuilders.JavaAnnotationBuilder.ValueBuilder valueBuilder);
}
private static DomainBuilders.JavaAnnotationBuilder.ValueBuilder javaEnumBuilder(final String desc, final String value) {
return new DomainBuilders.JavaAnnotationBuilder.ValueBuilder() {
@Override
public Optional<Object> build(ClassesByTypeName importedClasses) {
return Optional.<Object>of(
new DomainBuilders.JavaEnumConstantBuilder()
.withDeclaringClass(importedClasses.get(Type.getType(desc).getClassName()))
.withName(value)
.build());
}
};
}
private static class AnnotationTypeConversion {
private static final Map<String, Class<?>> externalTypeToInternalType = ImmutableMap.of(
Type.class.getName(), JavaClass.class,
Class.class.getName(), JavaClass.class,
Class[].class.getName(), JavaClass[].class
);
private static final Map<Class<?>, Function<Object, DomainBuilders.JavaAnnotationBuilder.ValueBuilder>> importedValueToInternalValue =
ImmutableMap.<Class<?>, Function<Object, DomainBuilders.JavaAnnotationBuilder.ValueBuilder>>of(
Type.class, new Function<Object, DomainBuilders.JavaAnnotationBuilder.ValueBuilder>() {
@Override
public DomainBuilders.JavaAnnotationBuilder.ValueBuilder apply(final Object input) {
return new DomainBuilders.JavaAnnotationBuilder.ValueBuilder() {
@Override
public Optional<Object> build(ClassesByTypeName importedClasses) {
return Optional.<Object>of(importedClasses.get(((Type) input).getClassName()));
}
};
}
}
);
static Class<?> convert(Class<?> type) {
return externalTypeToInternalType.containsKey(type.getName()) ?
externalTypeToInternalType.get(type.getName()) :
type;
}
static Optional<Class<?>> tryConvert(String typeName) {
return externalTypeToInternalType.containsKey(typeName) ?
Optional.<Class<?>>of(externalTypeToInternalType.get(typeName)) :
Optional.<Class<?>>absent();
}
static DomainBuilders.JavaAnnotationBuilder.ValueBuilder convert(Object value) {
return importedValueToInternalValue.containsKey(value.getClass()) ?
importedValueToInternalValue.get(value.getClass()).apply(value) :
DomainBuilders.JavaAnnotationBuilder.ValueBuilder.ofFinished(value);
}
}
}