001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.proxy2.stub;
019
020import java.lang.annotation.Annotation;
021
022import org.apache.commons.proxy2.interceptor.InterceptorUtils;
023
024public abstract class BaseAnnotationTrainer<S extends BaseAnnotationTrainer<S, A>, A extends Annotation> extends
025        BaseTrainer<S, A>
026{
027    protected BaseAnnotationTrainer()
028    {
029        super();
030    }
031
032    protected BaseAnnotationTrainer(Class<A> traineeType)
033    {
034        super(traineeType);
035    }
036
037    protected class WhenAnnotation<R> extends WhenObject<R>
038    {
039        public S thenStub(Class<R> type)
040        {
041            trainingContext().push(type);
042            trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
043            return self();
044        }
045
046        @Override
047        public S thenStub(BaseTrainer<?, R> trainer)
048        {
049            final R trainee = trainingContext().push(trainer.traineeType);
050            trainer.train(trainee);
051            trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
052            return self();
053        }
054    }
055
056    protected class WhenAnnotationArray<R> extends WhenObjectArray<R>
057    {
058        protected WhenAnnotationArray(Class<? extends R> componentType)
059        {
060            super(componentType);
061        }
062
063        @Override
064        public StubAnnotationArrayBuilder<R> thenBuildArray()
065        {
066            return new StubAnnotationArrayBuilder<R>(componentType);
067        }
068    }
069
070    protected class StubAnnotationArrayBuilder<R> extends StubArrayBuilder<R>
071    {
072        private final BaseTrainer<?, R> annotationTypeTrainer;
073
074        private <N extends Annotation> StubAnnotationArrayBuilder(final Class<? extends R> componentType)
075        {
076            super(componentType);
077            
078            /*
079             * We know the only type of array method that can be hosted on an annotation is an annotation array.
080             * Therefore we declare a bogus annotation type parameter on this method which we use to create
081             * our AnnotationTypeTrainer, whose type parameter requires an annotation type. N == R
082             */
083            @SuppressWarnings("unchecked") // we assume N == R
084            final Class<N> annotationType = (Class<N>) componentType;
085            @SuppressWarnings("unchecked") // and cast it back
086            final BaseTrainer<?, R> trainer = (BaseTrainer<?, R>) new AnnotationTypeTrainer<N>(
087                    annotationType);
088            this.annotationTypeTrainer = trainer;
089        }
090
091        @Override
092        public StubAnnotationArrayBuilder<R> addElement(BaseTrainer<?, R> trainer)
093        {
094            final R trainee = trainingContext().push(trainer.traineeType);
095
096            annotationTypeTrainer.train(trainee);
097            trainer.train(trainee);
098
099            elements.add(trainingContext().<R> pop());
100            return this;
101        }
102    }
103
104    @Override
105    public <R> WhenAnnotation<R> when(R expression)
106    {
107        return new WhenAnnotation<R>();
108    }
109
110    @Override
111    public <R> WhenAnnotationArray<R> when(R[] expression)
112    {
113        @SuppressWarnings("unchecked") // we can reasonably say that the component type of an R[] is Class<? extends R>:
114        final Class<? extends R> componentType = (Class<? extends R>) expression.getClass().getComponentType();
115        return new WhenAnnotationArray<R>(componentType);
116    }
117}