View Javadoc

1   /*
2    * Copyright 2012 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.callbackparams.internal.template.annotation;
18  
19  import java.lang.annotation.Annotation;
20  import java.lang.annotation.Retention;
21  import java.lang.annotation.RetentionPolicy;
22  import java.lang.reflect.AnnotatedElement;
23  import java.lang.reflect.Field;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  import org.callbackparams.ext.WrappingCallbackImpls;
28  import org.callbackparams.wrap.legacy.Wrapping;
29  
30  /**
31   * @author Henrik Kaipe
32   */
33  enum WrappingClassLocator {
34  
35      IMPL_ON_ENUM_CONSTANT {
36          @Override List<Class<? extends Wrapping>[]> locateClasses(Object value) {
37              return asWrappingClassList(asEnumConstantField(value)
38                      .getAnnotation(WrappingCallbackImpls.class));
39          }
40      },
41  
42      @CanBeSubclassedByClassSpecifiedImpl
43      IMPL_ON_ENUM_CONSTANT_ANNOTATION {
44          @Override List<Class<? extends Wrapping>[]> locateClasses(Object value) {
45              List<Class<? extends Wrapping>[]> wrappingList =
46                      new ArrayList<Class<? extends Wrapping>[]>();
47              for (Annotation a : asEnumConstantField(value).getAnnotations()) {
48                  final WrappingCallbackImpls implsOnAnnotation = a
49                          .annotationType()
50                          .getAnnotation(WrappingCallbackImpls.class);
51                  if (null != implsOnAnnotation) {
52                      wrappingList.add(implsOnAnnotation.value());
53                  }
54              }
55              return wrappingList;
56          }
57      },
58  
59      IMPL_ON_ENUM_CLASS_OR_VALUE_CLASS {
60  
61          private WrappingCallbackImpls findImplOn(Class<?> c) {
62              for (Annotation a : c.getDeclaredAnnotations()) {
63                  if (WrappingCallbackImpls.class == a.annotationType()) {
64                      return (WrappingCallbackImpls) a;
65                  }
66              }
67              return null;
68          }
69  
70          @Override List<Class<? extends Wrapping>[]> locateClasses(Object value) {
71              WrappingCallbackImpls wrappingImpl = findImplOn(value.getClass());
72              if (null == wrappingImpl) {
73                  AnnotatedElement possibleEnumField = asEnumConstantField(value);
74                  if (possibleEnumField instanceof Field) {
75                      wrappingImpl = findImplOn(
76                              ((Field)possibleEnumField).getDeclaringClass());
77                  }
78              }
79              return asWrappingClassList(wrappingImpl);
80          }
81      },
82  
83      @CanBeSubclassedByClassSpecifiedImpl
84      IMPL_ON_CLASS_ANNOTATION_OR_IMPL_ON_SUPER_CLASS {
85  
86          @Override List<Class<? extends Wrapping>[]> locateClasses(Object value) {
87              Class<?> c = value.getClass();
88              final WrappingCallbackImpls superClassImpl =
89                      c.getAnnotation(WrappingCallbackImpls.class);
90              final List<Class<? extends Wrapping>[]> wrappingList =
91                      new ArrayList<Class<? extends Wrapping>[]>();
92  
93              LOOP_THROUGH_CLASS_HIERARCY: do {
94                  for (Annotation a : c.getDeclaredAnnotations()) {
95                      if (a == superClassImpl) {
96                          break LOOP_THROUGH_CLASS_HIERARCY;
97                      } else {
98                          WrappingCallbackImpls annotationImpl = a.annotationType()
99                                  .getAnnotation(WrappingCallbackImpls.class);
100                         if (null != annotationImpl) {
101                             wrappingList.add(annotationImpl.value());
102                         }
103                     }
104                 }
105                 if (false == wrappingList.isEmpty()) {
106                     return wrappingList;
107                 }
108             } while (Object.class != (c = c.getSuperclass()));
109             return asWrappingClassList(superClassImpl);
110         }
111     };
112 
113     private static final AnnotatedElement NOT_AN_ENUM_CONSTANT =
114             new AnnotatedElement() {
115         private final Annotation[] NO_ANNOTATIONS = {};
116 
117         public <T extends Annotation> T getAnnotation(
118                 Class<T> annotationClass) { return null; }
119         public Annotation[] getAnnotations() { return NO_ANNOTATIONS; }
120         public Annotation[] getDeclaredAnnotations() { return NO_ANNOTATIONS; }
121         public boolean isAnnotationPresent(
122                 Class<? extends Annotation> annotationClass) { return false; }
123     };
124 
125     private static AnnotatedElement asEnumConstantField(Object value) {
126         if (value instanceof Enum<?>) {
127             final Enum<?> enumConstant = (Enum<?>) value;
128             try {
129                 Field enumConstantField = enumConstant.getDeclaringClass()
130                         .getField(enumConstant.name());
131                 try {
132                     enumConstantField.setAccessible(true);
133                 } catch (SecurityException ignoreAndTryToContinueAnyway) {}
134                 if (enumConstantField.isEnumConstant()
135                         && enumConstant == enumConstantField.get(null)) {
136                     return enumConstantField;
137                 }
138             } catch (Throwable ignore) {
139                 /*
140                  * This can happen if the specified value is not really an
141                  * enum-constant - in spite of being of type Enum<?>.
142                  */
143             }
144         }
145         return NOT_AN_ENUM_CONSTANT;
146     }
147 
148     private static List<Class<? extends Wrapping>[]> asWrappingClassList(
149             WrappingCallbackImpls possibleImpl) {
150         if (null == possibleImpl) {
151             return Collections.<Class<? extends Wrapping>[]>emptyList();
152         } else {
153             return Collections.singletonList(possibleImpl.value());
154         }
155     }
156 
157     boolean canBeSubclassedByClassSpecifiedImpl() {
158         return asEnumConstantField(this)
159                 .isAnnotationPresent(CanBeSubclassedByClassSpecifiedImpl.class);
160     }
161 
162     abstract List<Class<? extends Wrapping>[]> locateClasses(Object value);
163 
164     @Retention(RetentionPolicy.RUNTIME)
165     @interface CanBeSubclassedByClassSpecifiedImpl {}
166 }