/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 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.drools.compiler.rule.builder.util;
import org.drools.compiler.lang.descr.AnnotationDescr;
import org.drools.core.base.TypeResolver;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.drools.core.util.StringUtils.ucFirst;
public class AnnotationFactory {
public static Annotation buildAnnotation(TypeResolver typeResolver, AnnotationDescr annotationDescr) {
try {
Class<?> annotationClass = typeResolver.resolveType(annotationDescr.getFullyQualifiedName());
return buildAnnotation(annotationDescr, annotationClass);
} catch (ClassNotFoundException e) {
return null;
}
}
public static Annotation buildAnnotation(AnnotationDescr annotationDescr, Class<?> annotationClass) {
return (Annotation) newProxyInstance(annotationClass.getClassLoader(),
new Class<?>[]{Annotation.class, annotationClass},
new AnnotationInvocationHandler(annotationClass, annotationDescr));
}
public static class AnnotationInvocationHandler implements InvocationHandler {
private final Class<?> annotationClass;
private final AnnotationDescr annotationDescr;
public AnnotationInvocationHandler(Class<?> annotationClass, AnnotationDescr annotationDescr) {
this.annotationClass = annotationClass;
this.annotationDescr = annotationDescr;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("annotationType")) {
return annotationClass;
}
if (method.getName().equals("toString")) {
return toString();
}
if (method.getName().equals("hashCode")) {
return hashCode();
}
if (method.getName().equals("equals")) {
return annotationDescr.equals(args[0]);
}
try {
annotationClass.getMethod(method.getName());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Invoked not existing method " + method.getName() + " on annotation " + annotationClass.getName());
}
Object value = annotationDescr.getValue(method.getName());
return value == null ? method.getDefaultValue() : normalizeResult(method.getReturnType(), value);
}
@Override
public int hashCode() {
return annotationDescr.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof AnnotationInvocationHandler &&
annotationDescr.equals(((AnnotationInvocationHandler)obj).annotationDescr);
}
@Override
public String toString() {
return annotationDescr.toString();
}
private Object normalizeResult(Class<?> resultClass, Object val) {
if ( resultClass == String.class ) {
String value = val.toString();
if ( annotationDescr.isStrict() ) {
// quotes on a String value of a strict annotation are required
if (value.charAt(0) == '"' && value.charAt(value.length()-1) == '"') {
return value.substring(1, value.length() - 1);
} else {
throw new RuntimeException("Cannot convert " + value + " to an instance of type " + resultClass.getName());
}
} else {
return value;
}
}
if (resultClass.isInstance( val )) {
return val;
}
String value = val.toString();
if (resultClass == Boolean.class || resultClass == boolean.class) {
return Boolean.valueOf(value);
}
if (resultClass == Integer.class || resultClass == int.class) {
return Integer.valueOf(value);
}
if (resultClass.isEnum()) {
String annotationHead = resultClass.getSimpleName() + ".";
int typePos = value.indexOf(annotationHead);
if (typePos >= 0) {
value = value.substring(typePos+annotationHead.length());
}
try {
return Enum.valueOf((Class<Enum>) resultClass, value);
} catch (IllegalArgumentException e) {
if ( !annotationDescr.isStrict() ) {
value = ucFirst(value);
try {
return Enum.valueOf((Class<Enum>) resultClass, value);
} catch (IllegalArgumentException e2) {
return Enum.valueOf((Class<Enum>) resultClass, value.toUpperCase());
}
}
throw e;
}
}
throw new RuntimeException("Cannot convert " + value + " to an instance of type " + resultClass.getName());
}
}
}