View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.proxy2.stub;
19  
20  import java.lang.annotation.Annotation;
21  
22  import org.apache.commons.proxy2.interceptor.InterceptorUtils;
23  
24  public abstract class BaseAnnotationTrainer<S extends BaseAnnotationTrainer<S, A>, A extends Annotation> extends
25          BaseTrainer<S, A>
26  {
27      protected BaseAnnotationTrainer()
28      {
29          super();
30      }
31  
32      protected BaseAnnotationTrainer(Class<A> traineeType)
33      {
34          super(traineeType);
35      }
36  
37      protected class WhenAnnotation<R> extends WhenObject<R>
38      {
39          public S thenStub(Class<R> type)
40          {
41              trainingContext().push(type);
42              trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
43              return self();
44          }
45  
46          @Override
47          public S thenStub(BaseTrainer<?, R> trainer)
48          {
49              final R trainee = trainingContext().push(trainer.traineeType);
50              trainer.train(trainee);
51              trainingContext().then(InterceptorUtils.constant(trainingContext().pop(AnnotationInvoker.INSTANCE)));
52              return self();
53          }
54      }
55  
56      protected class WhenAnnotationArray<R> extends WhenObjectArray<R>
57      {
58          protected WhenAnnotationArray(Class<? extends R> componentType)
59          {
60              super(componentType);
61          }
62  
63          @Override
64          public StubAnnotationArrayBuilder<R> thenBuildArray()
65          {
66              return new StubAnnotationArrayBuilder<R>(componentType);
67          }
68      }
69  
70      protected class StubAnnotationArrayBuilder<R> extends StubArrayBuilder<R>
71      {
72          private final BaseTrainer<?, R> annotationTypeTrainer;
73  
74          private <N extends Annotation> StubAnnotationArrayBuilder(final Class<? extends R> componentType)
75          {
76              super(componentType);
77              
78              /*
79               * We know the only type of array method that can be hosted on an annotation is an annotation array.
80               * Therefore we declare a bogus annotation type parameter on this method which we use to create
81               * our AnnotationTypeTrainer, whose type parameter requires an annotation type. N == R
82               */
83              @SuppressWarnings("unchecked") // we assume N == R
84              final Class<N> annotationType = (Class<N>) componentType;
85              @SuppressWarnings("unchecked") // and cast it back
86              final BaseTrainer<?, R> trainer = (BaseTrainer<?, R>) new AnnotationTypeTrainer<N>(
87                      annotationType);
88              this.annotationTypeTrainer = trainer;
89          }
90  
91          @Override
92          public StubAnnotationArrayBuilder<R> addElement(BaseTrainer<?, R> trainer)
93          {
94              final R trainee = trainingContext().push(trainer.traineeType);
95  
96              annotationTypeTrainer.train(trainee);
97              trainer.train(trainee);
98  
99              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 }