)
{
/*
TODO: Caching!
*/
CompatibilityModeEnum compatibilityMode = getDocument().getConfiguration().getCompatibilityMode();
if(compatibilityMode == CompatibilityModeEnum.Passthrough) // No check required.
return;
if(feature instanceof Collection<?>)
{
for(Object featureItem : (Collection<?>)feature)
{checkCompatibility(featureItem);}
return;
}
Version featureVersion;
if(feature instanceof VersionEnum) // Explicit version.
{featureVersion = ((VersionEnum)feature).getVersion();}
else // Implicit version (element annotation).
{
PDF annotation;
{
if(feature instanceof String) // Property name.
{
BeanInfo classInfo;
try
{classInfo = Introspector.getBeanInfo(getClass());}
catch(IntrospectionException e)
{throw new RuntimeException(e);}
for(PropertyDescriptor property : classInfo.getPropertyDescriptors())
{
if(feature.equals(property.getName()))
{
feature = property.getReadMethod();
break;
}
}
}
else if(feature instanceof Enum<?>) // Enum constant.
{
try
{feature = feature.getClass().getField(((Enum<?>)feature).name());}
catch(Exception e)
{throw new RuntimeException(e);}
}
//TODO:remove?
// else if(feature == null) // Implicit feature.
// {
// try
// {
// /*
// NOTE: I know this is a somewhat weird (considering both OO design and performance) technique,
// but at the moment I couldn't figure out a better solution for dynamically retrieving
// the caller context's annotations in case of no arguments.
// */
// StackTraceElement callerStackElement = (StackTraceElement)Thread.currentThread().getStackTrace()[2];
// Class<?> callerClass = Class.forName(callerStackElement.getClassName());
// String callerMethodName = callerStackElement.getMethodName();
// for(Method method : callerClass.getMethods())
// {
// if(method.getName().equals(callerMethodName))
// {
// feature = method; // NOTE: I assume that any overload of the same method conforms to the same version.
// break;
// }
// }
// }
// catch(Exception e)
// {throw new RuntimeException(e);}
// }
if(!(feature instanceof AnnotatedElement))
throw new IllegalArgumentException("Feature type '" + feature.getClass().getName() + "' not supported.");
while(true)
{
annotation = ((AnnotatedElement)feature).getAnnotation(PDF.class);
if(annotation != null)
break;
if(feature instanceof Member)
{feature = ((Member)feature).getDeclaringClass();}
else if(feature instanceof Class<?>)
{
Class<?> containerClass = ((Class<?>)feature).getDeclaringClass();
feature = (containerClass != null ? containerClass : ((Class<?>)feature).getPackage());
}
else // Element hierarchy walk complete.
return; // NOTE: As no annotation is available, we assume the feature has no specific compatibility requirements.
}
}
featureVersion = annotation.value().getVersion();
}
// Is the feature version compatible?
if(getDocument().getVersion().compareTo(featureVersion) >= 0)
return;
// The feature version is NOT compatible: how to solve the conflict?
switch(compatibilityMode)
{
case Loose: // Accepts the feature version.
// Synchronize the document version!
getDocument().setVersion(featureVersion);
break;
case Strict: // Refuses the feature version.
// Throw a violation to the document version!
throw new RuntimeException("Incompatible feature (version " + featureVersion + " was required against document version " + getDocument().getVersion());
default:
throw new NotImplementedException("Unhandled compatibility mode: " + compatibilityMode.name());
}
}