use of org.apache.tapestry5.commons.services.CoercionTuple in project tapestry-5 by apache.
the class TypeCoercerImpl method queueIntermediates.
/**
* Creates and adds to the pool a new set of coercions based on an intermediate tuple. Adds
* compound coercion tuples
* to the end of the queue.
*
* @param sourceType
* the source type of the coercion
* @param targetType
* TODO
* @param intermediateTuple
* a tuple that converts from the source type to some intermediate type (that is not
* assignable to the target type)
* @param consideredTuples
* set of tuples that have already been added to the pool (directly, or as a compound
* coercion)
* @param queue
* the work queue of tuples
*/
@SuppressWarnings("unchecked")
private void queueIntermediates(Class sourceType, Class targetType, CoercionTuple intermediateTuple, Set<CoercionTuple.Key> consideredTuples, LinkedList<CoercionTuple> queue) {
Class intermediateType = intermediateTuple.getTargetType();
for (Class c : new InheritanceSearch(intermediateType)) {
for (CoercionTuple tuple : getTuples(c, targetType)) {
if (consideredTuples.contains(tuple.getKey())) {
continue;
}
Class newIntermediateType = tuple.getTargetType();
if (sourceType.isAssignableFrom(newIntermediateType)) {
continue;
}
// The intermediateTuple coercer gets from S --> I1 (an intermediate type).
// The current tuple's coercer gets us from I2 --> X. where I2 is assignable
// from I1 (i.e., I2 is a superclass/superinterface of I1) and X is a new
// intermediate type, hopefully closer to our eventual target type.
Coercion compoundCoercer = new CompoundCoercion(intermediateTuple.getCoercion(), tuple.getCoercion());
CoercionTuple compoundTuple = new CoercionTuple(sourceType, newIntermediateType, compoundCoercer, false);
// So, every tuple that is added to the queue can take as input the sourceType.
// The target type may be another intermediate type, or may be something
// assignable to the target type, which will bring the search to a successful
// conclusion.
queue.addLast(compoundTuple);
consideredTuples.add(tuple.getKey());
}
}
}
use of org.apache.tapestry5.commons.services.CoercionTuple in project tapestry-5 by apache.
the class TypeCoercerImpl method findOrCreateCoercion.
/**
* Here's the real meat; we do a search of the space to find coercions, or a system of
* coercions, that accomplish
* the desired coercion.
*
* There's <strong>TREMENDOUS</strong> room to improve this algorithm. For example, inheritance lists could be
* cached. Further, there's probably more ways to early prune the search. However, even with dozens or perhaps
* hundreds of tuples, I suspect the search will still grind to a conclusion quickly.
*
* The order of operations should help ensure that the most efficient tuple chain is located. If you think about how
* tuples are added to the queue, there are two factors: size (the number of steps in the coercion) and
* "class distance" (that is, number of steps up the inheritance hiearchy). All the appropriate 1 step coercions
* will be considered first, in class distance order. Along the way, we'll queue up all the 2 step coercions, again
* in class distance order. By the time we reach some of those, we'll have begun queueing up the 3 step coercions, and
* so forth, until we run out of input tuples we can use to fabricate multi-step compound coercions, or reach a
* final response.
*
* This does create a good number of short lived temporary objects (the compound tuples), but that's what the GC is
* really good at.
*
* @param sourceType
* @param targetType
* @return coercer from sourceType to targetType
*/
@SuppressWarnings("unchecked")
private Coercion findOrCreateCoercion(Class sourceType, Class targetType) {
if (sourceType == Void.class) {
return searchForNullCoercion(targetType);
}
// Trying to find exact match.
Optional<CoercionTuple> maybeTuple = getTuples(sourceType, targetType).stream().filter((t) -> sourceType.equals(t.getSourceType()) && targetType.equals(t.getTargetType())).findFirst();
if (maybeTuple.isPresent()) {
return maybeTuple.get().getCoercion();
}
// These are instance variables because this method may be called concurrently.
// On a true race, we may go to the work of seeking out and/or fabricating
// a tuple twice, but it's more likely that different threads are looking
// for different source/target coercions.
Set<CoercionTuple.Key> consideredTuples = CollectionFactory.newSet();
LinkedList<CoercionTuple> queue = CollectionFactory.newLinkedList();
seedQueue(sourceType, targetType, consideredTuples, queue);
while (!queue.isEmpty()) {
CoercionTuple tuple = queue.removeFirst();
// If the tuple results in a value type that is assignable to the desired target type,
// we're done! Later, we may add a concept of "cost" (i.e. number of steps) or
// "quality" (how close is the tuple target type to the desired target type). Cost
// is currently implicit, as compound tuples are stored deeper in the queue,
// so simpler coercions will be located earlier.
Class tupleTargetType = tuple.getTargetType();
if (targetType.isAssignableFrom(tupleTargetType)) {
return tuple.getCoercion();
}
// So .. this tuple doesn't get us directly to the target type.
// However, it *may* get us part of the way. Each of these
// represents a coercion from the source type to an intermediate type.
// Now we're going to look for conversions from the intermediate type
// to some other type.
queueIntermediates(sourceType, targetType, tuple, consideredTuples, queue);
}
throw new CoercionNotFoundException(String.format("Could not find a coercion from type %s to type %s.", sourceType.getName(), targetType.getName()), buildCoercionCatalog(), sourceType, targetType);
}
use of org.apache.tapestry5.commons.services.CoercionTuple in project tapestry-5 by apache.
the class EnumValueEncoderTest method roundtrip_with_custom_coercer.
@Test
public // TAP5-2496
void roundtrip_with_custom_coercer() {
CoercionTuple<Stooge, String> stoogeToString = CoercionTuple.create(Stooge.class, String.class, new Coercion<Stooge, String>() {
@Override
public String coerce(Stooge input) {
return String.valueOf(input.ordinal());
}
});
CoercionTuple<String, Stooge> stringToStooge = CoercionTuple.create(String.class, Stooge.class, new Coercion<String, Stooge>() {
@Override
public Stooge coerce(String input) {
return Stooge.values()[Integer.parseInt(input)];
}
});
Map<CoercionTuple.Key, CoercionTuple> map = new HashMap<>();
map.put(stoogeToString.getKey(), stoogeToString);
map.put(stringToStooge.getKey(), stringToStooge);
TypeCoercer typeCoercer = new TypeCoercerImpl(map);
EnumValueEncoder<Stooge> encoder = new EnumValueEncoder<Stooge>(typeCoercer, Stooge.class);
Stooge serverValue = Stooge.LARRY;
String clientValue = encoder.toClient(serverValue);
Stooge convertedBack = encoder.toValue(clientValue);
assertEquals(convertedBack, serverValue);
}
use of org.apache.tapestry5.commons.services.CoercionTuple in project tapestry-5 by apache.
the class BasicTypeCoercions method provideBasicTypeCoercions.
/**
* Provides the basic type coercions to a {@link MappedConfiguration} instance.
*/
public static void provideBasicTypeCoercions(MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration) {
add(configuration, Object.class, String.class, new Coercion<Object, String>() {
@Override
public String coerce(Object input) {
return input.toString();
}
});
add(configuration, Object.class, Boolean.class, new Coercion<Object, Boolean>() {
@Override
public Boolean coerce(Object input) {
return input != null;
}
});
add(configuration, String.class, Double.class, new Coercion<String, Double>() {
@Override
public Double coerce(String input) {
return Double.valueOf(input);
}
});
// String to BigDecimal is important, as String->Double->BigDecimal would lose
// precision.
add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>() {
@Override
public BigDecimal coerce(String input) {
return new BigDecimal(input);
}
});
add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>() {
@Override
public Double coerce(BigDecimal input) {
return input.doubleValue();
}
});
add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>() {
@Override
public BigInteger coerce(String input) {
return new BigInteger(input);
}
});
add(configuration, String.class, Long.class, new Coercion<String, Long>() {
@Override
public Long coerce(String input) {
return Long.valueOf(input);
}
});
add(configuration, String.class, Integer.class, Integer::valueOf);
add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>() {
@Override
public Byte coerce(Long input) {
return input.byteValue();
}
});
add(configuration, Long.class, Short.class, new Coercion<Long, Short>() {
@Override
public Short coerce(Long input) {
return input.shortValue();
}
});
add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>() {
@Override
public Integer coerce(Long input) {
return input.intValue();
}
});
add(configuration, Number.class, Long.class, new Coercion<Number, Long>() {
@Override
public Long coerce(Number input) {
return input.longValue();
}
});
add(configuration, Double.class, Float.class, new Coercion<Double, Float>() {
@Override
public Float coerce(Double input) {
return input.floatValue();
}
});
add(configuration, Long.class, Double.class, new Coercion<Long, Double>() {
@Override
public Double coerce(Long input) {
return input.doubleValue();
}
});
add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>() {
@Override
public Boolean coerce(String input) {
String trimmed = input == null ? "" : input.trim();
if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0)
return false;
return true;
}
});
add(configuration, Number.class, Boolean.class, new Coercion<Number, Boolean>() {
@Override
public Boolean coerce(Number input) {
return input.longValue() != 0;
}
});
add(configuration, Void.class, Boolean.class, new Coercion<Void, Boolean>() {
@Override
public Boolean coerce(Void input) {
return false;
}
});
add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>() {
@Override
public Boolean coerce(Collection input) {
return !input.isEmpty();
}
});
add(configuration, Object.class, List.class, new Coercion<Object, List>() {
@Override
public List coerce(Object input) {
return Collections.singletonList(input);
}
});
add(configuration, Object[].class, List.class, new Coercion<Object[], List>() {
@Override
public List coerce(Object[] input) {
return Arrays.asList(input);
}
});
add(configuration, Object[].class, Boolean.class, new Coercion<Object[], Boolean>() {
@Override
public Boolean coerce(Object[] input) {
return input != null && input.length > 0;
}
});
add(configuration, Float.class, Double.class, new Coercion<Float, Double>() {
@Override
public Double coerce(Float input) {
return input.doubleValue();
}
});
Coercion primitiveArrayCoercion = new Coercion<Object, List>() {
@Override
public List<Object> coerce(Object input) {
int length = Array.getLength(input);
Object[] array = new Object[length];
for (int i = 0; i < length; i++) {
array[i] = Array.get(input, i);
}
return Arrays.asList(array);
}
};
add(configuration, byte[].class, List.class, primitiveArrayCoercion);
add(configuration, short[].class, List.class, primitiveArrayCoercion);
add(configuration, int[].class, List.class, primitiveArrayCoercion);
add(configuration, long[].class, List.class, primitiveArrayCoercion);
add(configuration, float[].class, List.class, primitiveArrayCoercion);
add(configuration, double[].class, List.class, primitiveArrayCoercion);
add(configuration, char[].class, List.class, primitiveArrayCoercion);
add(configuration, boolean[].class, List.class, primitiveArrayCoercion);
add(configuration, String.class, File.class, new Coercion<String, File>() {
@Override
public File coerce(String input) {
return new File(input);
}
});
add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>() {
@Override
public TimeInterval coerce(String input) {
return new TimeInterval(input);
}
});
add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>() {
@Override
public Long coerce(TimeInterval input) {
return input.milliseconds();
}
});
add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>() {
@Override
public Object[] coerce(Object input) {
return new Object[] { input };
}
});
add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>() {
@Override
public Object[] coerce(Collection input) {
return input.toArray();
}
});
CoercionTuple<Flow, List> flowToListCoercion = CoercionTuple.create(Flow.class, List.class, Flow::toList);
configuration.add(flowToListCoercion.getKey(), flowToListCoercion);
CoercionTuple<Flow, Boolean> flowToBooleanCoercion = CoercionTuple.create(Flow.class, Boolean.class, (i) -> !i.isEmpty());
configuration.add(flowToBooleanCoercion.getKey(), flowToBooleanCoercion);
}
use of org.apache.tapestry5.commons.services.CoercionTuple in project tapestry-5 by apache.
the class TypeCoercerImpl method seedQueue.
/**
* Seeds the pool with the initial set of coercions for the given type.
*/
private void seedQueue(Class sourceType, Class targetType, Set<CoercionTuple.Key> consideredTuples, LinkedList<CoercionTuple> queue) {
for (Class c : new InheritanceSearch(sourceType)) {
List<CoercionTuple> tuples = getTuples(c, targetType);
if (tuples == null) {
continue;
}
for (CoercionTuple tuple : tuples) {
queue.addLast(tuple);
consideredTuples.add(tuple.getKey());
}
if (sourceType == Void.class) {
return;
}
}
}
Aggregations