View Javadoc

1   package net.sf.dobo;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.FileWriter;
6   import java.io.IOException;
7   import java.io.InputStreamReader;
8   import java.io.Writer;
9   
10  import java.lang.annotation.Annotation;
11  import java.lang.annotation.ElementType;
12  import java.lang.annotation.Target;
13  import java.lang.reflect.Field;
14  import java.lang.reflect.Method;
15  import java.lang.reflect.Modifier;
16  
17  import java.util.ArrayList;
18  import java.util.Arrays;
19  import java.util.HashSet;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Set;
23  
24  
25  /***
26   * Dobo utilities of dobo.
27   *
28   * @author arif
29   * @version 1.0
30   */
31  public class Dobo {
32      /***
33       * Creates a new Dobo object.
34       */
35      private Dobo() {
36      }
37  
38      /***
39       * To check wether the ContextImplementationObject (CIO) conform the Context
40       * contract. Throw RuntimeException if check failed to verified
41       * contextImplementationObject
42       *
43       * @param contextImplementationObject
44       *            clazz of Context Implementation Object.
45       */
46      @SuppressWarnings("unchecked")
47      public static void check(final Class<?> contextImplementationObject) {
48          Set<Class<?extends Annotation>> annotations = findAllContextInContextImplementationObject(contextImplementationObject);
49  
50          for (final Class<?extends Annotation> annotationType : annotations) {
51              if (annotationType.isAnnotationPresent(Context.class)) {
52                  Context context = annotationType.getAnnotation(Context.class);
53                  Class<?> contextInterface = context.value();
54                  Class<?>[] classMember = annotationType.getClasses();
55                  List<Class<?extends Annotation>> contextMembers = new ArrayList<Class<?extends Annotation>>();
56  
57                  for (final Class<?> cls : classMember) {
58                      if (cls.isAnnotation()) {
59                          contextMembers.add((Class<?extends Annotation>) cls);
60                      }
61                  }
62  
63                  for (final Class<?extends Annotation> contextMember : contextMembers) {
64                      // validate target object is map all the class member
65                      Target target = contextMember.getAnnotation(Target.class);
66                      List<ElementType> elementType = Arrays.asList(target.value());
67  
68                      if (elementType.contains(ElementType.METHOD)) {
69                          String name = contextMember.getSimpleName();
70                          Class<?>[] parameterTypes = new Class<?>[] {  };
71  
72                          if (contextMember.isAnnotationPresent(ContextMemberMethod.class)) {
73                              ContextMemberMethod contextMemberMethod = contextMember.getAnnotation(ContextMemberMethod.class);
74  
75                              if (!contextMemberMethod.name().isEmpty()) {
76                                  name = contextMemberMethod.name();
77                              }
78  
79                              if (contextMemberMethod.parameterType().length > 0) {
80                                  parameterTypes = contextMemberMethod.parameterType();
81                              }
82                          }
83  
84                          Method method = getInterfaceMethodMatchWith(name, parameterTypes,
85                                  context.value());
86  
87                          if (method == null) {
88                              throw new RuntimeException("\n# " + contextMember +
89                                  " not match with any method in " + contextInterface + ".\n" +
90                                  "Please check wether :" +
91                                  "\n* The @ContextMemberType conform the Context Interface Method return type" +
92                                  "\n* The @ContextMemberMethod name anda parameter type conform Context Interface Method name and parameter");
93                          }
94  
95                          if (contextMember.isAnnotationPresent(ContextMemberType.class)) {
96                              ContextMemberType contextMemberType = contextMember.getAnnotation(ContextMemberType.class);
97                              Class<?> expectedReturnType = method.getReturnType();
98  
99                              if (!expectedReturnType.isAssignableFrom(contextMemberType.value())) {
100                                 throw new RuntimeException("\n# " + contextMember +
101                                     " not match with method in " + contextInterface + ".\n" +
102                                     "Please check wether :" +
103                                     "\n* The @ContextMemberType conform the Context Interface Method return type" +
104                                     "\n* Expected " + expectedReturnType + " but found " +
105                                     contextMemberType.value());
106                             }
107                         }
108                     }
109 
110                     if (!((elementType.contains(ElementType.FIELD) &&
111                             (getFieldAnnotatedWith(contextMember, contextImplementationObject) != null)) ||
112                             (elementType.contains(ElementType.METHOD) &&
113                             (getMethodAnnotatedWith(contextMember, contextImplementationObject) != null)))) {
114                         throw new RuntimeException("\n# " + contextMember +
115                             " not match with any method in " + contextImplementationObject + ".\n" +
116                             "Please check wether :" + "\n* " + contextImplementationObject +
117                             " has method annotated with " + contextMember +
118                             (contextMember.isAnnotationPresent(ContextMemberType.class)
119                             ? ("\n* The method annotated with " + contextMember +
120                             " has return type " +
121                             contextMember.getAnnotation(ContextMemberType.class).value()) : "") +
122                             (contextMember.isAnnotationPresent(ContextMemberMethod.class)
123                             ? ("\n* The method annotated with " + contextMember +
124                             " has parameter type same with parameter type declared in @ContextMemberMethod")
125                             : ""));
126                     }
127                 }
128             }
129         }
130     }
131 
132     /***
133      * Find all context in ContextImplementationObject. By checking methods that
134      * annotated with context member.
135      *
136      * @param contextImplementationObject
137      * @return set of context
138      */
139     @SuppressWarnings("unchecked")
140     public static Set<Class<?extends Annotation>> findAllContextInContextImplementationObject(
141         final Class<?> contextImplementationObject) {
142         Set<Method> methods = new HashSet<Method>();
143         methods.addAll(Arrays.asList(contextImplementationObject.getDeclaredMethods()));
144         methods.addAll(Arrays.asList(contextImplementationObject.getMethods()));
145 
146         Set<Class<?extends Annotation>> contextAnnotations = new HashSet<Class<?extends Annotation>>();
147 
148         for (final Method method : methods) {
149             Annotation[] methodAnnotations = method.getAnnotations();
150 
151             for (final Annotation annotation : methodAnnotations) {
152                 Class<?> possibleContext = annotation.annotationType().getEnclosingClass();
153 
154                 if (possibleContext.isAnnotation() &&
155                         possibleContext.isAnnotationPresent(Context.class)) {
156                     contextAnnotations.add((Class<?extends Annotation>) possibleContext);
157                 }
158             }
159         }
160 
161         return contextAnnotations;
162     }
163 
164     /***
165      * To find method which is annotated with a Context Member. Return null if
166      * no Method match with the ContextMember
167      *
168      * @param contextMember
169      *
170      * @param contextImplementationObject
171      *
172      * @return method matched Context Implementation Object
173      */
174     @SuppressWarnings("unchecked")
175     public static Method getMethodAnnotatedWith(final Class<?extends Annotation> contextMember,
176         final Class<?> contextImplementationObject) {
177         List<ElementType> target = Arrays.asList(contextMember.getAnnotation(Target.class).value());
178 
179         if (!target.contains(ElementType.METHOD)) {
180             throw new IllegalArgumentException("Annotation " + contextMember.getName() +
181                 " not for element type METHOD ");
182         }
183 
184         Set<Method> methods = new HashSet<Method>();
185         methods.addAll(Arrays.asList(contextImplementationObject.getDeclaredMethods()));
186         methods.addAll(Arrays.asList(contextImplementationObject.getMethods()));
187 
188         for (final Method method : methods) {
189             if (method.isAnnotationPresent(contextMember)) {
190                 boolean dataTypeMatch = true;
191                 boolean memberTypeMatch = true;
192 
193                 // validate datatype
194                 if (contextMember.isAnnotationPresent(ContextMemberType.class)) {
195                     ContextMemberType contextMemberType = contextMember.getAnnotation(ContextMemberType.class);
196 
197                     if (!contextMemberType.value().isAssignableFrom(method.getReturnType())) {
198                         dataTypeMatch = false;
199                     }
200                 }
201 
202                 // validate member
203                 if (contextMember.isAnnotationPresent(ContextMemberMethod.class)) {
204                     ContextMemberMethod memberToCheck = contextMember.getAnnotation(ContextMemberMethod.class);
205                     Class<?>[] classes = memberToCheck.parameterType();
206                     Class<?>[] methodParameters = method.getParameterTypes();
207 
208                     if (classes.length != methodParameters.length) {
209                         memberTypeMatch = false;
210                     } else {
211                         for (int i = 0; i < classes.length; i++) {
212                             Class<?> crazz = classes[i];
213                             Class<?> parameterClass = methodParameters[i];
214 
215                             if (!parameterClass.isAssignableFrom(crazz)) {
216                                 memberTypeMatch = false;
217                             }
218                         }
219                     }
220                 }
221 
222                 // check wether both are match
223                 if (dataTypeMatch && memberTypeMatch) {
224                     return method;
225                 }
226             }
227         }
228 
229         return null;
230     }
231 
232     /***
233      * To Find a Field in Context Implementation Object which annotated with
234      * Context Member. Return null if no Field found.
235      *
236      * @param contextMember
237      * @param contextImplementationObject
238      * @return Field yang dianotasikan oleh context member
239      */
240     @SuppressWarnings("unchecked")
241     public static Field getFieldAnnotatedWith(final Class<?extends Annotation> contextMember,
242         final Class<?> contextImplementationObject) {
243         List<ElementType> target = Arrays.asList(contextMember.getAnnotation(Target.class).value());
244 
245         if (!target.contains(ElementType.FIELD)) {
246             throw new IllegalArgumentException("Annotation " + contextMember.getName() +
247                 " not for element type FIELD ");
248         }
249 
250         Set<Field> fields = new HashSet<Field>();
251         fields.addAll(Arrays.asList(contextImplementationObject.getDeclaredFields()));
252         fields.addAll(Arrays.asList(contextImplementationObject.getFields()));
253 
254         for (final Field field : fields) {
255             if (field.isAnnotationPresent(contextMember)) {
256                 boolean dataTypeMatch = true;
257 
258                 // validate datatype
259                 if (contextMember.isAnnotationPresent(ContextMemberType.class)) {
260                     ContextMemberType contextMemberType = contextMember.getAnnotation(ContextMemberType.class);
261 
262                     if (!contextMemberType.value().isAssignableFrom(field.getType())) {
263                         dataTypeMatch = false;
264                     }
265                 }
266 
267                 // check wether both are match
268                 if (dataTypeMatch) {
269                     return field;
270                 }
271             }
272         }
273 
274         return null;
275     }
276 
277     /***
278      * Instantiate Context Implementation Object Proxy according to Context
279      * Implementation Object.
280      *
281      * @param contextImplementationObject
282      * @param context
283      * @return Proxy object dari context implementation
284      */
285     public static Object instantiate(final Object contextImplementationObject,
286         final Class<?extends Annotation> context) {
287         return ContextImplementationObjectProxyCglib.newInstance(contextImplementationObject,
288             context);
289     }
290 
291     /***
292      * To find a method in Context Interface which match with the
293      * ContextMemberMethod. Return null if no Method match.
294      *
295      * @param contextMemberMethod
296      *
297      * @param contextInterface
298      *
299      * @return method yang match dengan context member method.
300      */
301     public static Method getInterfaceMethodMatchWith(final String name,
302         final Class<?>[] parameterType, final Class<?> contextInterface) {
303         try {
304             return contextInterface.getMethod(name, parameterType);
305         } catch (final SecurityException e) {
306             e.printStackTrace();
307         } catch (final NoSuchMethodException e) {
308             e.printStackTrace();
309         }
310 
311         return null;
312     }
313 
314     /***
315      * To Find ContextMember match with Context Interface Method.Return null if
316      * no ContextMember found.
317      *
318      * @param context
319      * @param contextInterfaceMethod
320      * @return Context Member.
321      */
322     @SuppressWarnings("unchecked")
323     public static Class<?extends Annotation> getContextMemberMatchWith(
324         final Class<?extends Annotation> context, final Method contextInterfaceMethod) {
325         Class<?>[] dirtyMemberClasses = context.getClasses();
326         List<Class<?extends Annotation>> memberClasses = new ArrayList<Class<?extends Annotation>>();
327 
328         for (final Class<?> dirtyMemberClass : dirtyMemberClasses) {
329             if (dirtyMemberClass.isAnnotation()) {
330                 Class<?extends Annotation> cleanMemberClass = (Class<?extends Annotation>) dirtyMemberClass;
331                 memberClasses.add(cleanMemberClass);
332             }
333         }
334 
335         for (final Class<?extends Annotation> memberClass : memberClasses) {
336             // what to do with member class ??
337             boolean matchWithContextMemberMethod = false;
338             boolean matchWithDataType = false;
339             String methodName = memberClass.getSimpleName();
340             Class<?>[] parameterClass = new Class[] {  };
341 
342             if (memberClass.isAnnotationPresent(ContextMemberMethod.class)) {
343                 ContextMemberMethod contextMemberMethod = memberClass.getAnnotation(ContextMemberMethod.class);
344 
345                 if (!contextMemberMethod.name().isEmpty()) {
346                     methodName = contextMemberMethod.name();
347                 }
348 
349                 if (contextMemberMethod.parameterType().length > 0) {
350                     parameterClass = contextMemberMethod.parameterType();
351                 }
352             }
353 
354             // matching the member method
355             if (contextInterfaceMethod.getName().equals(methodName) &&
356                     Arrays.equals(parameterClass, contextInterfaceMethod.getParameterTypes())) {
357                 matchWithContextMemberMethod = true;
358             }
359 
360             Class<?> expectedResult = void.class;
361 
362             if (memberClass.isAnnotationPresent(ContextMemberType.class)) {
363                 ContextMemberType contextMemberType = memberClass.getAnnotation(ContextMemberType.class);
364                 expectedResult = contextMemberType.value();
365             }
366 
367             if (contextInterfaceMethod.getReturnType().isAssignableFrom(expectedResult)) {
368                 matchWithDataType = true;
369             }
370 
371             if (matchWithContextMemberMethod && matchWithDataType) {
372                 return memberClass;
373             }
374         }
375 
376         return null;
377     }
378 
379     /***
380      * Generating context
381      *
382      * @param writer
383      *            to write result
384      * @param contextInterface
385      *            class of the context interface
386      * @param packageName
387      *            name of the result package
388      * @param contextName
389      *            name of the result context class
390      *
391      * @throws IOException
392      *             DOCUMENT ME!
393      */
394     private static void generateContext(final Writer writer, final Class<?> contextInterface,
395         final String packageName, final String contextName)
396         throws IOException {
397         writer.write("package " + packageName + ";\n");
398         writer.write("import java.lang.annotation.ElementType;\n");
399         writer.write("import java.lang.annotation.Retention;\n");
400         writer.write("import java.lang.annotation.RetentionPolicy;\n");
401         writer.write("import java.lang.annotation.Target;\n");
402         writer.write("import net.sf.dobo.Context;\n");
403         writer.write("import net.sf.dobo.ContextMemberMethod;\n");
404         writer.write("import net.sf.dobo.ContextMemberType;\n");
405         writer.write("\n");
406 
407         writer.write("@Context(" + contextInterface.getName() + ".class)\n");
408         writer.write("public @interface " + contextName + "{\n");
409 
410         Method[] methods = contextInterface.getMethods();
411 
412         for (final Method method : methods) {
413             if (Modifier.isAbstract(method.getModifiers())) {
414                 writer.write("\n");
415                 writer.write("\t@Target(ElementType.METHOD)\n");
416                 writer.write("\t@Retention(RetentionPolicy.RUNTIME)\n");
417 
418                 String returnType = method.getReturnType().getName() + ".class";
419 
420                 if (method.getReturnType().isArray()) {
421                     returnType = method.getReturnType().getComponentType().getName() + "[].class";
422                 }
423 
424                 writer.write("\t@ContextMemberType(" + returnType + ")\n");
425 
426                 StringBuilder builder = new StringBuilder();
427                 Iterator<Class<?>> parameterTypes = Arrays.asList(method.getParameterTypes())
428                                                           .iterator();
429 
430                 while (parameterTypes.hasNext()) {
431                     Class<?> class1 = (Class<?>) parameterTypes.next();
432                     String className = class1.getName() + ".class";
433 
434                     if (class1.isArray()) {
435                         className = class1.getComponentType().getName() + "[].class";
436                     }
437 
438                     builder.append(className);
439 
440                     if (parameterTypes.hasNext()) {
441                         builder.append(",");
442                     }
443                 }
444 
445                 writer.write("\t@ContextMemberMethod(name=\"" + method.getName() +
446                     "\",parameterType={" + builder.toString() + "})\n");
447                 writer.write("\tpublic @interface " + method.getName() + "{}\n");
448             }
449         }
450 
451         writer.write("\n");
452         writer.write("}\n");
453     }
454 
455     /***
456      * Creating context template
457      *
458      * @param args
459      * @throws ClassNotFoundException
460      * @throws IOException
461      * @throws ClassNotFoundException
462      * @throws IOException
463      */
464     public static void main(final String[] args) throws ClassNotFoundException, IOException {
465         String contextInterface = "";
466         String contextOutputPath = ".";
467         boolean createContext = true;
468 
469         while (createContext) {
470             BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
471 
472             System.out.print("1) Interface class (\"" + contextInterface + "\"): ");
473 
474             String contextInterface_;
475 
476             try {
477                 contextInterface_ = reader.readLine();
478             } catch (final IOException e) {
479                 throw new RuntimeException("Cannot read line ", e);
480             }
481 
482             if ((contextInterface_ != null) && !contextInterface_.isEmpty()) {
483                 contextInterface = contextInterface_;
484             }
485 
486             System.out.print("2) Context output (\"" + contextOutputPath + "\"): ");
487 
488             String contextOutputPath_ = "";
489 
490             try {
491                 contextOutputPath_ = reader.readLine();
492             } catch (final IOException e) {
493                 e.printStackTrace();
494                 throw new RuntimeException("Cannot read line " + e);
495             }
496 
497             if ((contextOutputPath_ != null) && !contextOutputPath_.isEmpty()) {
498                 contextOutputPath = contextOutputPath_;
499             }
500 
501             Class<?> contextInterfaceClass = Class.forName(contextInterface);
502             String contextName = contextInterfaceClass.getSimpleName() + "Context";
503 
504             System.out.print("3) Context Name (\"" + contextName + "\"): ");
505 
506             String contextName_;
507 
508             try {
509                 contextName_ = reader.readLine();
510             } catch (final IOException e1) {
511                 e1.printStackTrace();
512                 throw new RuntimeException("Cannot read line ", e1);
513             }
514 
515             if ((contextName_ != null) && !contextName_.isEmpty()) {
516                 contextName = contextName_;
517             }
518 
519             String packageName = contextInterfaceClass.getPackage().getName();
520             System.out.print("4) Package (\"" + packageName + "\"): ");
521 
522             String packageName_ = reader.readLine();
523 
524             if ((packageName_ != null) && !packageName_.isEmpty()) {
525                 packageName = packageName_;
526             }
527 
528             File file = new File(contextOutputPath + File.separator + contextName + ".java");
529 
530             if (file.exists()) {
531                 if (file.delete()) {
532                     System.out.println("File Deleted");
533                 }
534             } else {
535                 if (!file.getParentFile().isDirectory()) {
536                     try {
537                         file.getParentFile().mkdirs();
538                     } catch (final SecurityException se) {
539                         se.printStackTrace();
540                     }
541                 }
542             }
543 
544             if (file.createNewFile()) {
545                 FileWriter writer = new FileWriter(file);
546                 generateContext(writer, contextInterfaceClass, packageName, contextName);
547                 writer.flush();
548 
549                 System.out.println("New context created (" + file + ")");
550                 System.out.print("would you like create different context (yes) : ");
551             }
552 
553             String createContext_;
554 
555             try {
556                 createContext_ = reader.readLine();
557 
558                 if ((createContext_ != null) && !createContext_.isEmpty()) {
559                     createContext = Boolean.valueOf(createContext_);
560                 }
561             } catch (final IOException e) {
562                 e.printStackTrace();
563             }
564         }
565     }
566 }