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
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
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
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
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
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
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
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
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 }