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.support.core.impl;
import com.servicerocket.confluence.randombits.support.core.convert.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.*;
/**
* The default implementation of {@link ConversionAssistant}.
*/
public class DefaultConversionAssistant implements ConversionAssistant {
/**
* This class acts as a key to identify conversions from one specific type to another.
*/
private static class Conversion {
private final Class source;
private final Class target;
public Conversion( Class source, Class target ) {
this.source = source;
this.target = target;
}
@Override
public int hashCode() {
return source.hashCode() + target.hashCode() + 7;
}
@Override
public boolean equals( Object o ) {
if ( o instanceof Conversion ) {
Conversion c = (Conversion) o;
return c.source == source && c.target == target;
}
return false;
}
@Override
public String toString() {
return "{" + source.getName() + " > " + target.getName() + "}";
}
}
/**
* Compares two converters by cost. Lower costs will be listed first.
*/
private final Comparator COST_COMPARATOR = new Comparator() {
@Override
public int compare( Converter converter1, Converter converter2 ) {
return converter1.getCost().compareTo( converter2.getCost() );
}
};
/**
* Always returns null. Used to cache a result for source/target combinations
* that have already been searched but no result was found.
*/
private final static Converter NULL_CONVERTER = new Converter() {
@Override
public Class getSourceType() {
return null;
}
@Override
public Class getTargetType() {
return null;
}
@Override
public ConversionCost getCost() {
return ConversionCost.FAIL;
}
@Override
public boolean canConvert( Class sourceType, Class targetType ) {
return false;
}
@Override
public boolean canConvert( Object source, Class targetType ) {
return false;
}
@Override
public T convert( Object source, Class targetType ) throws ConversionException {
return null;
}
};
// Contains the cache of converters that by their source type.
private final Map, Set> converterList;
// Contains the cache of conversions that have already been calculated.
private Map conversionCache;
@Autowired
public void setConverters(List converters) {
for(Converter converter : converters) {
addConverter(converter);
}
}
public DefaultConversionAssistant() {
conversionCache = new HashMap();
converterList = Collections.synchronizedMap( new HashMap, Set>());
}
/**
* Removes the specified converter and resets the caches.
*
* @param converter The converter to remove.
*/
protected void removeConverter( Converter converter ) {
synchronized (converterList) {
Set converterSet = converterList.get( converter.getSourceType() );
if ( converterSet != null ) {
converterSet.remove( converter );
if ( converterSet.isEmpty() )
converterList.remove( converter.getSourceType() );
}
}
resetConversions();
}
/**
* Adds the specified converter and resets the caches.
*
* @param converter The converter to add.
*/
public void addConverter( Converter converter ) {
synchronized (converterList) {
Set converterSet = converterList.get( converter.getSourceType() );
if ( converterSet == null ) {
converterSet = new HashSet();
converterList.put(converter.getSourceType(), converterSet);
}
converterSet.add( converter );
}
resetConversions();
}
/**
* Resets the conversion cache.
*/
private synchronized void resetConversions() {
conversionCache = new HashMap( 30 );
}
/**
* Checks if the source can be converted to the target type. Note that this
* is considered to be a 'can possibly convert' - it is possible that
* this method will return true, but the actual conversion will not
* occur due to some other issue, and null is returned from the {@link #convert(Object, Class)}
* method. However, in general, they will match up.
*
* @param source The source object.
* @param targetType The target type.
* @return true if there is a converter that handles that conversion.
*/
@Override
public boolean canConvert( Object source, Class targetType ) {
if ( source == null )
return false;
if ( targetType.isInstance( source ) )
return true;
Converter converter = findConverter( source, targetType );
return converter.canConvert( source, targetType );
}
@Override
public T convert( Object source, Class targetType ) throws ConversionException {
// Check for null
if ( source == null )
return null;
// Check if it's already the target type
if ( targetType.isInstance( source ) )
return targetType.cast( source );
// Check for a converter.
Converter converter = findConverter( source, targetType );
return converter.convert( source, targetType );
}
@Override
public void registerConverter(Converter converter) {
addConverter(converter);
}
@Override
public void unregisterConverter(Converter converter) {
removeConverter(converter);
}
private Converter findConverter( Object source, Class targetType ) {
Conversion conversion = new Conversion( source.getClass(), targetType );
Converter converter = conversionCache.get( conversion );
if ( converter == null ) {
// Try finding a registered converter
Set> visited = new HashSet>( 20 );
converter = scanConverter( conversion, visited );
}
return converter;
}
private Converter scanConverter( Class sourceType, Class targetType, Set> visited ) {
return scanConverter( new Conversion( sourceType, targetType ), visited );
}
private Converter scanConverter( Conversion conversion, Set> visited ) {
// First, make sure we haven't already cached one.
Converter bestConverter = conversionCache.get( conversion );
if ( bestConverter == null ) {
// Check we haven't already visited this type during this scan. If so, stop looking.
if ( !visited.add( conversion.source ) )
return null;
List foundConverters = new ArrayList( 10 );
// Search through the other converters for the source type
Set sourceConverters = converterList.get( conversion.source );
if ( sourceConverters != null ) {
for ( Converter sourceConverter : sourceConverters ) {
// No point searching further if the new converter is already more expensive than the current one.
if ( sourceConverter.canConvert( conversion.source, conversion.target ) ) {
// We have a match!
foundConverters.add( sourceConverter );
} else {
// We have to look further
Converter targetConverter = scanConverter( sourceConverter.getTargetType(), conversion.target, visited );
if ( targetConverter != null && targetConverter.canConvert( sourceConverter.getTargetType(), conversion.target ) ) {
foundConverters.add( new ChainedConverter( sourceConverter, targetConverter ) );
}
}
}
}
// Then, look through the source's interfaces
Class[] interfaces = conversion.source.getInterfaces();
for ( Class type : interfaces ) {
Converter interfaceConverter = scanConverter( type, conversion.target, visited );
if ( interfaceConverter != null && interfaceConverter.canConvert( type, conversion.target ) )
foundConverters.add( interfaceConverter );
}
// Next, try superclasses
if ( conversion.source.getSuperclass() != null ) {
Converter superclassConverter = scanConverter( conversion.source.getSuperclass(), conversion.target, visited );
if ( superclassConverter != null && superclassConverter.canConvert( conversion.source.getSuperclass(), conversion.target ) )
foundConverters.add( superclassConverter );
}
// Figure out what our converter should be for this conversion.
switch ( foundConverters.size() ) {
case 0:
// If all else has failed, cache a null converter.
bestConverter = NULL_CONVERTER;
break;
case 1:
// If there's just one, use it directly
bestConverter = foundConverters.get( 0 );
break;
default:
// Otherwise, sort the list by cost and provide them as alternatives.
Collections.sort( foundConverters, COST_COMPARATOR );
bestConverter = new AlternativeConverter( conversion.source, conversion.target, foundConverters );
}
// Cache the result.
conversionCache.put( conversion, bestConverter );
}
return bestConverter;
}
}