Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package com.servicerocket.confluence.randombits.supplier.core.annotate;
import com.servicerocket.confluence.randombits.supplier.core.KeyHandler;
import com.servicerocket.confluence.randombits.supplier.core.KeyPattern;
import com.servicerocket.confluence.randombits.supplier.core.Supplier;
import com.servicerocket.confluence.randombits.supplier.core.SupplierContext;
import java.lang.reflect.Method;
import java.util.*;
/**
* This is a base class for suppliers that wish to use annotated
* methods to define the {@link com.servicerocket.confluence.randombits.supplier.core.KeyHandler} instances available for this
* supplier. It does this by annotating the class and its methods with the following annotations:
*
*
*
{@link SupplierPrefix} - Added to a Supplier class to indicate the prefix value for all keys it handles.
*
{@link SupportedTypes} - Added to the Supplier class to indicate the supported types.
*
{@link SupplierKey} - Added to methods to indicate they handle specific key(s).
*
*
* A method that is marked as a {@link SupplierKey} acts as a receiver for specific 'key' values that match the
* rules for both the prefix and type identified by the {@link SupplierPrefix} and {@link SupportedTypes}. A method
* marked with {@link SupplierKey} can have zero or more parameters. All parameters must be annotated with one
* of the following:
*
*
*
{@link KeyValue} - The object being evaluated.
*
{@link KeyParam} - A String value that will receive a parameter as defined by the {@link SupplierKey} for that method.
*
{@link KeyContext} - A {@link com.servicerocket.confluence.randombits.supplier.core.SupplierContext} value which contains the current
* keychain execution context.
*
*
* Not every key handler method will need any or all of the above. The most common is {@link KeyValue}, but it's possible
* for more 'global' supplier (eg. {@link com.servicerocket.confluence.randombits.supplier.core.special.ValueSupplier}) to not require one.
*
* The method may be public, private, static, final, or whatever. It is generally not recommended to make abstract methods
* {@link SupplierKey}s because they would have to be re-annotated for every concrete method. A better option is to have
* a concrete, annotated method that calls a protected abstract method that subclasses will implement.
*
* To create a Supplier, simply subclass this class and
* create methods with the following pattern:
*
*
*/
public abstract class AnnotatedSupplier implements Supplier {
private final Set prefixes;
private final Set> supportedTypes;
private boolean prefixRequired;
private List keyDetails;
private boolean nullAllowed;
public AnnotatedSupplier() {
Set prefixes = new HashSet();
findPrefixes( prefixes, getClass() );
if ( prefixes.size() == 0 )
throw new IllegalStateException( "AnnotatedSupplier instances using the default constructor must provide a @SupplierPrefix annotation to determine the prefix." );
this.prefixes = Collections.unmodifiableSet( prefixes );
Set> supportedTypes = new HashSet>();
nullAllowed = findSupportedTypes( supportedTypes, getClass() );
this.supportedTypes = supportedTypes.size() == 0 ? null : Collections.unmodifiableSet( supportedTypes );
nullAllowed = nullAllowed || supportedTypes == null;
initKeyHandlers();
}
public AnnotatedSupplier( String prefix, boolean prefixRequired, Class[] supportedTypes, boolean allowNull ) {
this( Collections.singleton( prefix ), prefixRequired, supportedTypes, allowNull );
}
/**
* Constructs an annotated supplier with the prefix and supported type details hard-wired.
* Any {@link com.servicerocket.confluence.randombits.supplier.core.annotate.SupplierPrefix} or
* {@link com.servicerocket.confluence.randombits.supplier.core.annotate.SupportedTypes} annotations will be ignored.
*
* @param prefixes The prefixes to allow.
* @param prefixRequired true if the prefix must be used.
* @param supportedTypes The list of types supported by this Supplier. If all types (even null) are supported, do not provide any values.
* @param allowNull Is null allowed
*/
public AnnotatedSupplier( Set prefixes, boolean prefixRequired, Class[] supportedTypes, boolean allowNull ) {
this.prefixes = Collections.unmodifiableSet( prefixes );
this.prefixRequired = prefixRequired;
this.supportedTypes = asSet( supportedTypes );
this.nullAllowed = allowNull;
initKeyHandlers();
}
/**
* Finds all supported types, checking other the supertypes for inherited types.
*
* @param typeSet The set of types supported.
* @param type The type to check.
* @return true if the supplier should allow null values.
*/
private boolean findSupportedTypes( Set> typeSet, Class type ) {
SupportedTypes supportedTypes = type.getAnnotation( SupportedTypes.class );
boolean allowNull = false;
if ( supportedTypes != null ) {
typeSet.addAll( Arrays.asList( supportedTypes.value() ) );
if ( !supportedTypes.inherit() )
return supportedTypes.nullAllowed();
else
allowNull = supportedTypes.nullAllowed();
}
if ( type.getSuperclass() != null )
allowNull = allowNull || findSupportedTypes( typeSet, type.getSuperclass() );
return allowNull;
}
private void findPrefixes( Set prefixes, Class type ) {
SupplierPrefix prefix = type.getAnnotation( SupplierPrefix.class );
if ( prefix != null ) {
prefixes.addAll( Arrays.asList( prefix.value() ) );
if ( prefix.required() )
prefixRequired = true;
if ( !prefix.inherit() )
return;
}
if ( type.getSuperclass() != null )
findPrefixes( prefixes, type.getSuperclass() );
}
private Set asSet( T[] supportedTypes ) {
if ( supportedTypes != null && supportedTypes.length > 0 ) {
Set types = new HashSet();
types.addAll( Arrays.asList( supportedTypes ) );
return Collections.unmodifiableSet( types );
} else {
return null;
}
}
@Override
public Set> getSupportedTypes() {
return supportedTypes;
}
/**
* This method is called to check that an object is fully supported
* by this supplier. It should not have to check the type - the object must
* be one of the Class values returned by {@link #getSupportedTypes()}.
*
* The default implementation simply returns true.
* Subclasses should override this if they need to perform extra checks on
* specific instances of a value before it is handled.
*
* @param value The value to check.
* @return true if the value is supported.
*/
protected boolean checkSupportedValue( Object value ) {
return value != null || nullAllowed;
}
@Override
public boolean isNullAllowed() {
return nullAllowed;
}
@Override
public Set getPrefixes() {
return prefixes;
}
@Override
public boolean isPrefixRequired() {
return prefixRequired;
}
/**
* Retrieves the set of {@link com.servicerocket.confluence.randombits.supplier.core.KeyHandler} instances for this supplier.
* The first time the method is called, the set is created based on methods
* that are annotated with {@link SupplierKey}.
*
* @return the collection of {@link com.servicerocket.confluence.randombits.supplier.core.KeyHandler} instances for this supplier.
* @throws AnnotatedKeyDetailsException if there is a problem with any of the
* annotated methods that are used to create the key details.
*/
@Override
public Collection getKeyDetails() {
return keyDetails;
}
private void initKeyHandlers() {
List details = new ArrayList();
Class supplierClass = getClass();
while ( supplierClass != null ) {
for ( Method method : supplierClass.getDeclaredMethods() ) {
// Check if it's a key handler method.
SupplierKey key = method.getAnnotation( SupplierKey.class );
if ( key != null ) {
for ( String pattern : key.value() ) {
KeyHandler handler = createKeyHandler( pattern, method );
details.add( handler );
}
}
}
supplierClass = supplierClass.getSuperclass();
}
Collections.sort( details, AnnotatedKeyHandler.COMPARATOR );
keyDetails = Collections.unmodifiableList( details );
}
/**
* Override this method to change the {@link KeyHandler} instance created for the method.
* By default, this creates an instance of {@link AnnotatedKeyHandler}.
*
* @param pattern the key pattern.
* @param method the method to execute.
* @return the KeyHandler instance.
*/
protected KeyHandler createKeyHandler( String pattern, Method method ) {
return new AnnotatedKeyHandler( KeyPattern.parse( pattern ), method, this );
}
/**
* This method is called when values for {link KeyValue} are converted to the target type.
* By default, it simply asks the {@link SupplierContext} to do the conversion, but
* subclasses may override this if they want to do more complex conversion in their suppliers.
* If they do, they should also override the {@link #canGetValueAs(com.servicerocket.confluence.randombits.supplier.core.SupplierContext, Class)}
* method to match the same conversion conditions.
*
* @param context The supplier context.
* @param targetValueType The target type.
* @param The target type.
* @return The converted value, or null if none could be found.
* @see #canGetValueAs(com.servicerocket.confluence.randombits.supplier.core.SupplierContext, Class)
*/
protected T getValueAs( SupplierContext context, Class targetValueType ) {
return context.getValueAs( targetValueType );
}
/**
* This method is called when checking if the current value of the context can be
* returned or converted to the specified type. Subclasses should override this to match
* any custom conditions in a custom implementation of
* {@link #getValueAs(com.servicerocket.confluence.randombits.supplier.core.SupplierContext, Class)}
*
* @param context The context to check again.
* @param targetValueType The target value type.
* @return true if the value can be converted.
* @see #getValueAs(com.servicerocket.confluence.randombits.supplier.core.SupplierContext, Class)
*/
protected boolean canGetValueAs( SupplierContext context, Class targetValueType ) {
return context.canGetValueAs( targetValueType );
}
}