/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.teiid.designer.runtime.version.spi.ITeiidServerVersion;
import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version;
/**
*
*/
public class AnnotationUtils {
private AnnotationUtils() {}
/**
* @param klazz
* @param annotationClass
*
* @return given class has the given annotation
*/
public static boolean hasAnnotation(Class<?> klazz, Class<? extends Annotation> annotationClass) {
return klazz.isAnnotationPresent(annotationClass);
}
/**
* @param accessibleObject
* @param annotationClass
*
* @return given accessibleObject has the given annotation
*/
public static boolean hasAnnotation(AccessibleObject accessibleObject, Class<? extends Annotation> annotationClass) {
return accessibleObject.isAnnotationPresent(annotationClass);
}
/**
* @param enumValue
* @param annotationClass
*
* @return given enumValue has the given annotation on its field
*/
public static boolean hasAnnotation(Enum<?> enumValue, Class<? extends Annotation> annotationClass) {
try {
Field enumField = enumValue.getClass().getField(enumValue.name());
return hasAnnotation(enumField, annotationClass);
} catch (Exception ex) {
return false;
}
}
/**
* @param klazz
* @param annotationClass
*
* @return the annotation of the given class from the given class
*/
public static <T extends Annotation> T getAnnotation(Class<?> klazz, Class<T> annotationClass) {
return klazz.getAnnotation(annotationClass);
}
/**
* @param accessibleObject
* @param annotationClass
*
* @return the annotation of the given class from the given accessibleObject
*/
public static <T extends Annotation> T getAnnotation(AccessibleObject accessibleObject, Class<T> annotationClass) {
return accessibleObject.getAnnotation(annotationClass);
}
/**
* @param enumValue
* @param annotationClass
*
* @return the annotation of the given class from the given enumValue
*/
public static <T extends Annotation> T getAnnotation(Enum<?> enumValue, Class<T> annotationClass) {
try {
Field enumField = enumValue.getClass().getField(enumValue.name());
return getAnnotation(enumField, annotationClass);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* @param testVersion
* @param currentVersion
*
* @return testVersion is greater than or equal to the currentVersion's minimum version
*/
private static boolean isGreaterOrEqualThan(ITeiidServerVersion testVersion, ITeiidServerVersion currentVersion) {
// Ensure we have no wildcards in the current version
currentVersion = currentVersion.getMinimumVersion();
if (currentVersion.equals(testVersion) || currentVersion.isGreaterThan(testVersion))
return true;
return false;
}
/**
* <p>
* Tests the given {@link Removed} annotation's version against
* the given version. If the latter is greater than or equal to the
* former than the method returns true, false otherwise.
* <p>
* If removed contains wildcards then its a programming error.
* <p>
* The current version can contain wildcards as these are
* eliminated using {@link ITeiidServerVersion#getMinimumVersion()}
* <p>
* If the removed annotation is null then a value of false is returned
* since they is no annotation to test against and the most preferred
* outcome of uses of this test would be including the element that
* lacks the annotation.
*
* @param removed annotation
* @param currentVersion
*
* @return currentVersion >= removed value
*/
public static boolean isGreaterThanOrEqualTo(Removed removed, ITeiidServerVersion currentVersion) {
if (removed == null || currentVersion == null)
return false;
return isGreaterOrEqualThan(removed.value().get(), currentVersion);
}
/**
* <p>
* Tests the given {@link Since} annotation's version against
* the given version. If the latter is greater than or equal to the
* former then the method returns true false otherwise.
* <p>
* If since contains wildcards then its a programming error.
* <p>
* The current version can contain wildcards as these are
* eliminated using {@link ITeiidServerVersion#getMinimumVersion()}
* <p>
* If the since annotation is null then a value of true is returned
* since they is no annotation to test against and the most preferred
* outcome of uses of this test would be including the element that
* lacks the annotation.
*
* @param since annotation
* @param currentVersion
*
* @return currentVersion >= since value
*/
public static boolean isGreaterThanOrEqualTo(Since since, ITeiidServerVersion currentVersion) {
if (since == null || currentVersion == null)
return true;
return isGreaterOrEqualThan(since.value().get(), currentVersion);
}
/**
* Convenience function that draws on the other functions to give a single
* answer of whether the given object is applicable for the given teiid version
*
* @param obj
* @param currentVersion
* @return true if the given object is applicable for the given teiid version
*/
public static boolean isApplicable(Class<?> obj, ITeiidServerVersion currentVersion) {
if (hasAnnotation(obj, Removed.class)) {
Removed removed = getAnnotation(obj, Removed.class);
if (isGreaterThanOrEqualTo(removed, currentVersion)) {
return false;
}
}
if (hasAnnotation(obj, Since.class)) {
Since since = getAnnotation(obj, Since.class);
if (!isGreaterThanOrEqualTo(since, currentVersion)) {
return false;
}
}
return true;
}
/**
* Convenience function that draws on the other functions to give a single
* answer of whether the given object is applicable for the given teiid version
*
* @param obj
* @param currentVersion
* @return true if the given object is applicable for the given teiid version
*/
public static boolean isApplicable(AccessibleObject obj, ITeiidServerVersion currentVersion) {
if (hasAnnotation(obj, Removed.class)) {
Removed removed = getAnnotation(obj, Removed.class);
if (isGreaterThanOrEqualTo(removed, currentVersion)) {
return false;
}
}
if (hasAnnotation(obj, Since.class)) {
Since since = getAnnotation(obj, Since.class);
if (!isGreaterThanOrEqualTo(since, currentVersion)) {
return false;
}
}
return true;
}
/**
* Convenience function that draws on the other functions to give a single
* answer of whether the given object is applicable for the given teiid version
*
* @param obj
* @param currentVersion
* @return true if the given object is applicable for the given teiid version
*/
public static boolean isApplicable(Enum<?> obj, ITeiidServerVersion currentVersion) {
try {
if (hasAnnotation(obj, Removed.class)) {
Removed removed = getAnnotation(obj, Removed.class);
if (isGreaterThanOrEqualTo(removed, currentVersion)) {
return false;
}
}
} catch (Exception ex) {
return false;
}
try {
if (hasAnnotation(obj, Since.class)) {
Since since = getAnnotation(obj, Since.class);
if (!isGreaterThanOrEqualTo(since, currentVersion)) {
return false;
}
}
} catch (Exception ex) {
return false;
}
return true;
}
private static class UpdateVersionPair implements Comparable<UpdateVersionPair>{
private final ITeiidServerVersion version;
private final String replacedValue;
public UpdateVersionPair(ITeiidServerVersion version, String replacedValue) {
this.version = version;
this.replacedValue = replacedValue;
}
/**
* @return the version
*/
public ITeiidServerVersion getVersion() {
return this.version;
}
/**
* @return the replacedValue
*/
public String getReplacedValue() {
return this.replacedValue;
}
@Override
public int compareTo(UpdateVersionPair other) {
if (getVersion().equals(other.getVersion()))
return 0;
else if (getVersion().isLessThan(other.getVersion()))
return -1;
else
return 1;
}
}
/**
* @param enumValue
* @param updatedName
* @param currentVersion
*
* @return old name from {@link Updated} name or given updated name depending on teiid version
*/
public static String getUpdatedName(Enum<?> enumValue, String updatedName, ITeiidServerVersion currentVersion) {
if (! hasAnnotation(enumValue, Updated.class))
return updatedName;
Updated updated = getAnnotation(enumValue, Updated.class);
Version[] versions = updated.version();
String[] replacements = updated.replaces();
List<UpdateVersionPair> pairs = new ArrayList<UpdateVersionPair>();
for (int i = 0; i < versions.length; ++i) {
Version version = versions[i];
if (i >= replacements.length)
break;
String replaced = replacements[i];
pairs.add(new UpdateVersionPair(version.get(), replaced));
}
Collections.sort(pairs);
// Check biggest version and if current version is greater than
// it then returned the updated name since the latest value is
// the correct one.
UpdateVersionPair maxPair = pairs.get(pairs.size() - 1);
if (currentVersion.isGreaterThanOrEqualTo(maxPair.getVersion()))
return updatedName;
UpdateVersionPair rangePair = null;
for (UpdateVersionPair pair : pairs) {
if (rangePair == null) {
rangePair = pair;
continue;
}
if (currentVersion.isLessThan(pair.getVersion())) {
// the last rangePair will have been selected
break;
}
rangePair = pair;
if (currentVersion.equals(rangePair.getVersion())) {
// the new rangePair is the correct version
break;
}
// current version is greater than range pair so keep iterating
}
// Found appropriate version pair so return its replaced value
return rangePair.getReplacedValue();
}
}