/*
* Copyright 2011 <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ocpsoft.rewrite.annotation.handler;
import java.lang.reflect.Field;
import java.util.Iterator;
import org.ocpsoft.common.services.ServiceLoader;
import org.ocpsoft.common.util.Assert;
import org.ocpsoft.logging.Logger;
import org.ocpsoft.rewrite.annotation.Convert;
import org.ocpsoft.rewrite.annotation.api.FieldContext;
import org.ocpsoft.rewrite.annotation.api.HandlerChain;
import org.ocpsoft.rewrite.annotation.spi.FieldAnnotationHandler;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.exception.RewriteException;
import org.ocpsoft.rewrite.param.Converter;
import org.ocpsoft.rewrite.param.Parameter;
import org.ocpsoft.rewrite.param.ParameterConfiguration;
import org.ocpsoft.rewrite.spi.ConverterProvider;
/**
* Handler for {@link Convert}.
*
* @author Christian Kaltepoth
*/
public class ConvertHandler extends FieldAnnotationHandler<Convert>
{
private final Logger log = Logger.getLogger(ConvertHandler.class);
@Override
public Class<Convert> handles()
{
return Convert.class;
}
@Override
public int priority()
{
return HandlerWeights.WEIGHT_TYPE_ENRICHING;
}
@Override
public void process(FieldContext context, Convert annotation, HandlerChain chain)
{
Field field = context.getJavaField();
Parameter<?> parameter = (Parameter<?>) context.get(Parameter.class);
if (parameter != null) {
Converter<?> converter = null;
// identify converter by the type of the converter
if (annotation.with() != Object.class) {
converter = LazyConverterAdapter.forConverterType(annotation.with());
}
// identify converter by some kind of unique id
else if (annotation.id().length() > 0) {
converter = LazyConverterAdapter.forConverterId(annotation.id());
}
// default: identify converter by the target type
else {
converter = LazyConverterAdapter.forTargetType(field.getType());
}
if (parameter instanceof ParameterConfiguration)
((ParameterConfiguration<?>) parameter).convertedBy(converter);
else
throw new RewriteException("Cannot add @" + Convert.class.getSimpleName() + " to [" + field
+ "] of class [" + field.getDeclaringClass() + "] because the parameter ["
+ parameter.getName() + "] is not writable.");
if (log.isTraceEnabled()) {
log.trace("Attached converter to field [{}] of class [{}]: ", new Object[] {
field.getName(), field.getDeclaringClass().getName(), converter
});
}
}
// continue with the chain
chain.proceed();
}
/**
* This class uses the {@link ConverterProvider} SPI to lazily obtain the {@link Converter} for a given {@link Class}
* instance.
*/
private static class LazyConverterAdapter implements Converter<Object>
{
private final Class<?> targetType;
private final String converterId;
private final Class<?> converterType;
private LazyConverterAdapter(Class<?> targetType, String converterId, Class<?> converterType)
{
this.targetType = targetType;
this.converterId = converterId;
this.converterType = converterType;
}
public static LazyConverterAdapter forConverterType(Class<?> converterType)
{
return new LazyConverterAdapter(null, null, converterType);
}
public static LazyConverterAdapter forConverterId(String id)
{
return new LazyConverterAdapter(null, id, null);
}
public static LazyConverterAdapter forTargetType(Class<?> targetType)
{
return new LazyConverterAdapter(targetType, null, null);
}
@Override
@SuppressWarnings("unchecked")
public Object convert(Rewrite event, EvaluationContext context, Object value)
{
Converter<?> converter = null;
// let one of the SPI implementations build the converter
Iterator<ConverterProvider> providers = ServiceLoader.load(ConverterProvider.class).iterator();
while (providers.hasNext()) {
ConverterProvider provider = providers.next();
if (targetType != null) {
converter = provider.getByTargetType(targetType);
}
else if (converterType != null) {
converter = provider.getByConverterType(converterType);
}
else {
converter = provider.getByConverterId(converterId);
}
if (converter != null) {
break;
}
}
Assert.notNull(converter, "Got no converter from any ConverterProvider for: " + this.toString());
return converter.convert(event, context, value);
}
@Override
public String toString()
{
StringBuilder b = new StringBuilder();
b.append(this.getClass().getSimpleName());
b.append(" for ");
if (targetType != null) {
b.append(" target type ");
b.append(targetType.getName());
}
else if (converterType != null) {
b.append(" converter type ");
b.append(converterType.getName());
}
else {
b.append(" id ");
b.append(converterId);
}
return b.toString();
}
}
}