com.espertech.esper.epl.annotation.AnnotationUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of esper Show documentation
Show all versions of esper Show documentation
Complex event processing and event series analysis component
/*
* *************************************************************************************
* Copyright (C) 2006-2015 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
* *************************************************************************************
*/
package com.espertech.esper.epl.annotation;
import com.espertech.esper.client.EPStatementException;
import com.espertech.esper.client.annotation.Hint;
import com.espertech.esper.client.annotation.HintEnum;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.core.EngineImportException;
import com.espertech.esper.epl.core.EngineImportService;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.spec.AnnotationDesc;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.SimpleTypeCaster;
import com.espertech.esper.util.SimpleTypeCasterFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
/**
* Utility to handle EPL statement annotations.
*/
public class AnnotationUtil
{
private static final Log log = LogFactory.getLog(AnnotationUtil.class);
public static Map> mapByNameLowerCase(List annotations) {
Map> map = new HashMap>();
for (AnnotationDesc desc : annotations) {
String key = desc.getName().toLowerCase();
if (map.containsKey(key)) {
map.get(key).add(desc);
continue;
}
List annos = new ArrayList(2);
annos.add(desc);
map.put(key, annos);
}
return map;
}
public static Object getValue(AnnotationDesc desc) {
for (Pair pair : desc.getAttributes()) {
if (pair.getFirst().toLowerCase().equals("value")) {
return pair.getSecond();
}
}
return null;
}
/**
* Compile annotation objects from descriptors.
* @param annotationSpec spec for annotations
* @param engineImportService engine imports
* @param eplStatement statement expression
* @return annotations
*/
public static Annotation[] compileAnnotations(List annotationSpec, EngineImportService engineImportService, String eplStatement)
{
Annotation[] annotations;
try
{
annotations = AnnotationUtil.compileAnnotations(annotationSpec, engineImportService);
}
catch (AnnotationException e)
{
throw new EPStatementException("Failed to process statement annotations: " + e.getMessage(), e, eplStatement);
}
catch (RuntimeException ex)
{
String message = "Unexpected exception compiling annotations in statement, please consult the log file and report the exception: " + ex.getMessage();
log.error(message, ex);
throw new EPStatementException(message, ex, eplStatement);
}
return annotations;
}
/**
* Compiles annotations to an annotation array.
* @param desc a list of descriptors
* @param engineImportService for resolving the annotation class
* @return annotations or empty array if none
* @throws AnnotationException if annotations could not be created
*/
private static Annotation[] compileAnnotations(List desc, EngineImportService engineImportService)
throws AnnotationException
{
Annotation[] annotations = new Annotation[desc.size()];
for (int i = 0; i < desc.size(); i++)
{
annotations[i] = createProxy(desc.get(i), engineImportService);
if (annotations[i] instanceof Hint)
{
HintEnum.validateGetListed(annotations[i]);
}
}
return annotations;
}
private static Annotation createProxy(AnnotationDesc desc, EngineImportService engineImportService)
throws AnnotationException
{
// resolve class
final Class annotationClass;
try
{
annotationClass = engineImportService.resolveAnnotation(desc.getName());
}
catch (EngineImportException e)
{
throw new AnnotationException("Failed to resolve @-annotation class: " + e.getMessage());
}
// obtain Annotation class properties
List annotationAttributeLists = getAttributes(annotationClass);
Set allAttributes = new HashSet();
Set requiredAttributes = new LinkedHashSet();
for (AnnotationAttribute annotationAttribute : annotationAttributeLists)
{
allAttributes.add(annotationAttribute.getName());
if (annotationAttribute.getDefaultValue() != null)
{
requiredAttributes.add(annotationAttribute.getName());
}
}
// get attribute values
List providedValues = new ArrayList();
for (Pair annotationValuePair : desc.getAttributes())
{
providedValues.add(annotationValuePair.getFirst());
}
// for all attributes determine value
final Map properties = new LinkedHashMap();
for (AnnotationAttribute annotationAttribute : annotationAttributeLists)
{
// find value pair for this attribute
String attributeName = annotationAttribute.getName();
Pair pairFound = null;
for (Pair annotationValuePair : desc.getAttributes())
{
if (annotationValuePair.getFirst().equals(attributeName))
{
pairFound = annotationValuePair;
}
}
Object valueProvided = pairFound == null ? null : pairFound.getSecond();
Object value = getFinalValue(annotationClass, annotationAttribute, valueProvided, engineImportService);
properties.put(attributeName, value);
providedValues.remove(attributeName);
requiredAttributes.remove(attributeName);
}
if (requiredAttributes.size() > 0)
{
List required = new ArrayList(requiredAttributes);
Collections.sort(required);
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a value for attribute '" + required.iterator().next() + "'");
}
if (providedValues.size() > 0)
{
List provided = new ArrayList(providedValues);
Collections.sort(provided);
if (allAttributes.contains(provided.get(0)))
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' has duplicate attribute values for attribute '" + provided.get(0) + "'");
}
else
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' does not have an attribute '" + provided.get(0) + "'");
}
}
// return handler
InvocationHandler handler = new EPLAnnotationInvocationHandler(annotationClass, properties);
return (Annotation) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] {annotationClass}, handler);
}
private static Object getFinalValue(Class annotationClass, AnnotationAttribute annotationAttribute, Object value, EngineImportService engineImportService) throws AnnotationException
{
if (value == null)
{
if (annotationAttribute.getDefaultValue() == null)
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a value for attribute '" + annotationAttribute.getName() + "'");
}
return annotationAttribute.getDefaultValue();
}
// handle non-array
if (!annotationAttribute.getType().isArray())
{
// handle primitive value
if (!annotationAttribute.getType().isAnnotation())
{
// if expecting an enumeration type, allow string value
if (annotationAttribute.getType().isEnum() && JavaClassHelper.isImplementsInterface(value.getClass(), CharSequence.class)) {
String valueString = value.toString().trim();
// find case-sensitive exact match first
for (Object constant : annotationAttribute.getType().getEnumConstants()) {
Enum e = (Enum) constant;
if (e.name().equals(valueString)) {
return constant;
}
}
// find case-insensitive match
String valueUppercase = valueString.toUpperCase();
for (Object constant : annotationAttribute.getType().getEnumConstants()) {
Enum e = (Enum) constant;
if (e.name().toUpperCase().equals(valueUppercase)) {
return constant;
}
}
}
// cast as required
SimpleTypeCaster caster = SimpleTypeCasterFactory.getCaster(value.getClass(), annotationAttribute.getType());
Object finalValue = caster.cast(value);
if (finalValue == null)
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a " +
annotationAttribute.getType().getSimpleName() + "-typed value for attribute '" + annotationAttribute.getName() + "' but received " +
"a " + value.getClass().getSimpleName() + "-typed value");
}
return finalValue;
}
else
{
// nested annotation
if (!(value instanceof AnnotationDesc))
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a " +
annotationAttribute.getType().getSimpleName() + "-typed value for attribute '" + annotationAttribute.getName() + "' but received " +
"a " + value.getClass().getSimpleName() + "-typed value");
}
return createProxy((AnnotationDesc) value, engineImportService);
}
}
if (!value.getClass().isArray())
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a " +
annotationAttribute.getType().getSimpleName() + "-typed value for attribute '" + annotationAttribute.getName() + "' but received " +
"a " + value.getClass().getSimpleName() + "-typed value");
}
Object array = Array.newInstance(annotationAttribute.getType().getComponentType(), Array.getLength(value));
for (int i = 0; i < Array.getLength(value); i++)
{
Object arrayValue = Array.get(value, i);
if (arrayValue == null)
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a " +
"non-null value for array elements for attribute '" + annotationAttribute.getName() + "'");
}
SimpleTypeCaster caster = SimpleTypeCasterFactory.getCaster(arrayValue.getClass(), annotationAttribute.getType().getComponentType());
Object finalValue = caster.cast(arrayValue);
if (finalValue == null)
{
throw new AnnotationException("Annotation '" + annotationClass.getSimpleName() + "' requires a " +
annotationAttribute.getType().getComponentType().getSimpleName() + "-typed value for array elements for attribute '" + annotationAttribute.getName() + "' but received " +
"a " + arrayValue.getClass().getSimpleName() + "-typed value");
}
Array.set(array, i, finalValue);
}
return array;
}
private static List getAttributes(Class annotationClass)
{
List props = new ArrayList();
Method[] methods = annotationClass.getMethods();
if (methods == null)
{
return Collections.EMPTY_LIST;
}
for (int i = 0; i < methods.length; i++)
{
if (methods[i].getReturnType() == void.class)
{
continue;
}
if (methods[i].getParameterTypes().length > 0)
{
continue;
}
if ((methods[i].getName().equals("class")) ||
(methods[i].getName().equals("getClass")) ||
(methods[i].getName().equals("toString")) ||
(methods[i].getName().equals("annotationType")) ||
(methods[i].getName().equals("hashCode")))
{
continue;
}
props.add(new AnnotationAttribute(methods[i].getName(), methods[i].getReturnType(), methods[i].getDefaultValue()));
}
Collections.sort(props, new Comparator()
{
public int compare(AnnotationAttribute o1, AnnotationAttribute o2)
{
return o1.getName().compareTo(o2.getName());
}
});
return props;
}
public static Annotation findAnnotation(Annotation[] annotations, Class annotationClass) {
if (!annotationClass.isAnnotation()) {
throw new IllegalArgumentException("Class " + annotationClass.getName() + " is not an annotation class");
}
if (annotations == null || annotations.length == 0) {
return null;
}
for (Annotation anno : annotations) {
if (JavaClassHelper.isImplementsInterface(anno.getClass(), annotationClass)) {
return anno;
}
}
return null;
}
public static List findAnnotations(Annotation[] annotations, Class annotationClass) {
if (!annotationClass.isAnnotation()) {
throw new IllegalArgumentException("Class " + annotationClass.getName() + " is not an annotation class");
}
if (annotations == null || annotations.length == 0) {
return null;
}
List annotationsList = new ArrayList();
for (Annotation anno : annotations) {
if (JavaClassHelper.isImplementsInterface(anno.getClass(), annotationClass)) {
annotationsList.add(anno);
}
}
return annotationsList;
}
public static Annotation[] mergeAnnotations(Annotation[] first, Annotation[] second) {
return (Annotation[]) CollectionUtil.addArrays(first, second);
}
public static String getExpectSingleStringValue(String msgPrefix, List annotationsSameName) throws ExprValidationException {
if (annotationsSameName.size() > 1) {
throw new ExprValidationException(msgPrefix + " multiple annotations provided named '" + annotationsSameName.get(0).getName() + "'");
}
AnnotationDesc annotation = annotationsSameName.get(0);
Object value = AnnotationUtil.getValue(annotation);
if (value == null) {
throw new ExprValidationException(msgPrefix + " no value provided for annotation '" + annotation.getName() + "', expected a value");
}
if (!(value instanceof String)) {
throw new ExprValidationException(msgPrefix + " string value expected for annotation '" + annotation.getName() + "'");
}
return (String) value;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy