/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.statemachine.config.common.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.GenericTypeResolver;
/**
* A base class for {@link AnnotationConfigurer} that allows subclasses to only
* implement the methods they are interested in. It also provides a mechanism
* for using the {@link AnnotationConfigurer} and when done gaining access to the
* {@link AnnotationBuilder} that is being configured.
*
* @author Rob Winch
* @author Janne Valkealahti
*
* @param <O> The Object being built by B
* @param <I> The interface of type B
* @param <B> The Builder that is building O and is configured by {@link AnnotationConfigurerAdapter}
*/
public abstract class AnnotationConfigurerAdapter<O,I,B extends AnnotationBuilder<O>>
implements AnnotationConfigurer<O,B> {
private B builder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
@Override
public void init(B builder) throws Exception {}
@Override
public void configure(B builder) throws Exception {}
/**
* Return the {@link AnnotationBuilder} when done using the
* {@link AnnotationConfigurer}. This is useful for method chaining.
*
* @return the {@link AnnotationBuilder}
*/
@SuppressWarnings("unchecked")
public I and() {
// we're either casting to itself or its interface
return (I) getBuilder();
}
/**
* Gets the {@link AnnotationBuilder}. Cannot be null.
*
* @return the {@link AnnotationBuilder}
* @throws IllegalStateException if AnnotationBuilder is null
*/
protected final B getBuilder() {
if(builder == null) {
throw new IllegalStateException("annotationBuilder cannot be null");
}
return builder;
}
/**
* Adds an {@link ObjectPostProcessor} to be used for this adapter. The
* default implementation does nothing to the object.
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
*/
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
/**
* Sets the {@link AnnotationBuilder} to be used. This is automatically set
* when using
* {@link AbstractConfiguredAnnotationBuilder#apply(AnnotationConfigurerAdapter)}
*
* @param builder the {@link AnnotationBuilder} to set
*/
public void setBuilder(B builder) {
this.builder = builder;
}
/**
* An {@link ObjectPostProcessor} that delegates work to numerous
* {@link ObjectPostProcessor} implementations.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<? extends Object>> postProcessors = new ArrayList<ObjectPostProcessor<?>>();
@Override
public Object postProcess(Object object) {
for(ObjectPostProcessor opp : postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass,ObjectPostProcessor.class);
if(oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
}
return object;
}
/**
* Adds an {@link ObjectPostProcessor} to use
*
* @param objectPostProcessor the {@link ObjectPostProcessor} to add
* @return true if the {@link ObjectPostProcessor} was added, else false
*/
private boolean addObjectPostProcessor(ObjectPostProcessor<? extends Object> objectPostProcessor) {
return this.postProcessors.add(objectPostProcessor);
}
}
@Override
public boolean isAssignable(AnnotationBuilder<O> builder) {
return true;
}
}