package net.sf.dobo; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Dobo utilities of dobo. * * @author arif * @version 1.0 */ public class Dobo { /** * Creates a new Dobo object. */ private Dobo() { } /** * To check wether the ContextImplementationObject (CIO) conform the Context * contract. Throw RuntimeException if check failed to verified * contextImplementationObject * * @param contextImplementationObject * clazz of Context Implementation Object. */ @SuppressWarnings("unchecked") public static void check(final Class contextImplementationObject) { Set> annotations = findAllContextInContextImplementationObject(contextImplementationObject); for (final Class annotationType : annotations) { if (annotationType.isAnnotationPresent(Context.class)) { Context context = annotationType.getAnnotation(Context.class); Class contextInterface = context.value(); Class[] classMember = annotationType.getClasses(); List> contextMembers = new ArrayList>(); for (final Class cls : classMember) { if (cls.isAnnotation()) { contextMembers.add((Class) cls); } } for (final Class contextMember : contextMembers) { // validate target object is map all the class member Target target = contextMember.getAnnotation(Target.class); List elementType = Arrays.asList(target.value()); if (elementType.contains(ElementType.METHOD)) { String name = contextMember.getSimpleName(); Class[] parameterTypes = new Class[] { }; if (contextMember.isAnnotationPresent(ContextMemberMethod.class)) { ContextMemberMethod contextMemberMethod = contextMember.getAnnotation(ContextMemberMethod.class); if (!contextMemberMethod.name().isEmpty()) { name = contextMemberMethod.name(); } if (contextMemberMethod.parameterType().length > 0) { parameterTypes = contextMemberMethod.parameterType(); } } Method method = getInterfaceMethodMatchWith(name, parameterTypes, context.value()); if (method == null) { throw new RuntimeException("\n# " + contextMember + " not match with any method in " + contextInterface + ".\n" + "Please check wether :" + "\n* The @ContextMemberType conform the Context Interface Method return type" + "\n* The @ContextMemberMethod name anda parameter type conform Context Interface Method name and parameter"); } if (contextMember.isAnnotationPresent(ContextMemberType.class)) { ContextMemberType contextMemberType = contextMember.getAnnotation(ContextMemberType.class); Class expectedReturnType = method.getReturnType(); if (!expectedReturnType.isAssignableFrom(contextMemberType.value())) { throw new RuntimeException("\n# " + contextMember + " not match with method in " + contextInterface + ".\n" + "Please check wether :" + "\n* The @ContextMemberType conform the Context Interface Method return type" + "\n* Expected " + expectedReturnType + " but found " + contextMemberType.value()); } } } if (!((elementType.contains(ElementType.FIELD) && (getFieldAnnotatedWith(contextMember, contextImplementationObject) != null)) || (elementType.contains(ElementType.METHOD) && (getMethodAnnotatedWith(contextMember, contextImplementationObject) != null)))) { throw new RuntimeException("\n# " + contextMember + " not match with any method in " + contextImplementationObject + ".\n" + "Please check wether :" + "\n* " + contextImplementationObject + " has method annotated with " + contextMember + (contextMember.isAnnotationPresent(ContextMemberType.class) ? ("\n* The method annotated with " + contextMember + " has return type " + contextMember.getAnnotation(ContextMemberType.class).value()) : "") + (contextMember.isAnnotationPresent(ContextMemberMethod.class) ? ("\n* The method annotated with " + contextMember + " has parameter type same with parameter type declared in @ContextMemberMethod") : "")); } } } } } /** * Find all context in ContextImplementationObject. By checking methods that * annotated with context member. * * @param contextImplementationObject * @return set of context */ @SuppressWarnings("unchecked") public static Set> findAllContextInContextImplementationObject( final Class contextImplementationObject) { Set methods = new HashSet(); methods.addAll(Arrays.asList(contextImplementationObject.getDeclaredMethods())); methods.addAll(Arrays.asList(contextImplementationObject.getMethods())); Set> contextAnnotations = new HashSet>(); for (final Method method : methods) { Annotation[] methodAnnotations = method.getAnnotations(); for (final Annotation annotation : methodAnnotations) { Class possibleContext = annotation.annotationType().getEnclosingClass(); if (possibleContext.isAnnotation() && possibleContext.isAnnotationPresent(Context.class)) { contextAnnotations.add((Class) possibleContext); } } } return contextAnnotations; } /** * To find method which is annotated with a Context Member. Return null if * no Method match with the ContextMember * * @param contextMember * * @param contextImplementationObject * * @return method matched Context Implementation Object */ @SuppressWarnings("unchecked") public static Method getMethodAnnotatedWith(final Class contextMember, final Class contextImplementationObject) { List target = Arrays.asList(contextMember.getAnnotation(Target.class).value()); if (!target.contains(ElementType.METHOD)) { throw new IllegalArgumentException("Annotation " + contextMember.getName() + " not for element type METHOD "); } Set methods = new HashSet(); methods.addAll(Arrays.asList(contextImplementationObject.getDeclaredMethods())); methods.addAll(Arrays.asList(contextImplementationObject.getMethods())); for (final Method method : methods) { if (method.isAnnotationPresent(contextMember)) { boolean dataTypeMatch = true; boolean memberTypeMatch = true; // validate datatype if (contextMember.isAnnotationPresent(ContextMemberType.class)) { ContextMemberType contextMemberType = contextMember.getAnnotation(ContextMemberType.class); if (!contextMemberType.value().isAssignableFrom(method.getReturnType())) { dataTypeMatch = false; } } // validate member if (contextMember.isAnnotationPresent(ContextMemberMethod.class)) { ContextMemberMethod memberToCheck = contextMember.getAnnotation(ContextMemberMethod.class); Class[] classes = memberToCheck.parameterType(); Class[] methodParameters = method.getParameterTypes(); if (classes.length != methodParameters.length) { memberTypeMatch = false; } else { for (int i = 0; i < classes.length; i++) { Class crazz = classes[i]; Class parameterClass = methodParameters[i]; if (!parameterClass.isAssignableFrom(crazz)) { memberTypeMatch = false; } } } } // check wether both are match if (dataTypeMatch && memberTypeMatch) { return method; } } } return null; } /** * To Find a Field in Context Implementation Object which annotated with * Context Member. Return null if no Field found. * * @param contextMember * @param contextImplementationObject * @return Field yang dianotasikan oleh context member */ @SuppressWarnings("unchecked") public static Field getFieldAnnotatedWith(final Class contextMember, final Class contextImplementationObject) { List target = Arrays.asList(contextMember.getAnnotation(Target.class).value()); if (!target.contains(ElementType.FIELD)) { throw new IllegalArgumentException("Annotation " + contextMember.getName() + " not for element type FIELD "); } Set fields = new HashSet(); fields.addAll(Arrays.asList(contextImplementationObject.getDeclaredFields())); fields.addAll(Arrays.asList(contextImplementationObject.getFields())); for (final Field field : fields) { if (field.isAnnotationPresent(contextMember)) { boolean dataTypeMatch = true; // validate datatype if (contextMember.isAnnotationPresent(ContextMemberType.class)) { ContextMemberType contextMemberType = contextMember.getAnnotation(ContextMemberType.class); if (!contextMemberType.value().isAssignableFrom(field.getType())) { dataTypeMatch = false; } } // check wether both are match if (dataTypeMatch) { return field; } } } return null; } /** * Instantiate Context Implementation Object Proxy according to Context * Implementation Object. * * @param contextImplementationObject * @param context * @return Proxy object dari context implementation */ public static Object instantiate(final Object contextImplementationObject, final Class context) { return ContextImplementationObjectProxyCglib.newInstance(contextImplementationObject, context); } /** * To find a method in Context Interface which match with the * ContextMemberMethod. Return null if no Method match. * * @param contextMemberMethod * * @param contextInterface * * @return method yang match dengan context member method. */ public static Method getInterfaceMethodMatchWith(final String name, final Class[] parameterType, final Class contextInterface) { try { return contextInterface.getMethod(name, parameterType); } catch (final SecurityException e) { e.printStackTrace(); } catch (final NoSuchMethodException e) { e.printStackTrace(); } return null; } /** * To Find ContextMember match with Context Interface Method.Return null if * no ContextMember found. * * @param context * @param contextInterfaceMethod * @return Context Member. */ @SuppressWarnings("unchecked") public static Class getContextMemberMatchWith( final Class context, final Method contextInterfaceMethod) { Class[] dirtyMemberClasses = context.getClasses(); List> memberClasses = new ArrayList>(); for (final Class dirtyMemberClass : dirtyMemberClasses) { if (dirtyMemberClass.isAnnotation()) { Class cleanMemberClass = (Class) dirtyMemberClass; memberClasses.add(cleanMemberClass); } } for (final Class memberClass : memberClasses) { // what to do with member class ?? boolean matchWithContextMemberMethod = false; boolean matchWithDataType = false; String methodName = memberClass.getSimpleName(); Class[] parameterClass = new Class[] { }; if (memberClass.isAnnotationPresent(ContextMemberMethod.class)) { ContextMemberMethod contextMemberMethod = memberClass.getAnnotation(ContextMemberMethod.class); if (!contextMemberMethod.name().isEmpty()) { methodName = contextMemberMethod.name(); } if (contextMemberMethod.parameterType().length > 0) { parameterClass = contextMemberMethod.parameterType(); } } // matching the member method if (contextInterfaceMethod.getName().equals(methodName) && Arrays.equals(parameterClass, contextInterfaceMethod.getParameterTypes())) { matchWithContextMemberMethod = true; } Class expectedResult = void.class; if (memberClass.isAnnotationPresent(ContextMemberType.class)) { ContextMemberType contextMemberType = memberClass.getAnnotation(ContextMemberType.class); expectedResult = contextMemberType.value(); } if (contextInterfaceMethod.getReturnType().isAssignableFrom(expectedResult)) { matchWithDataType = true; } if (matchWithContextMemberMethod && matchWithDataType) { return memberClass; } } return null; } /** * Generating context * * @param writer * to write result * @param contextInterface * class of the context interface * @param packageName * name of the result package * @param contextName * name of the result context class * * @throws IOException * DOCUMENT ME! */ private static void generateContext(final Writer writer, final Class contextInterface, final String packageName, final String contextName) throws IOException { writer.write("package " + packageName + ";\n"); writer.write("import java.lang.annotation.ElementType;\n"); writer.write("import java.lang.annotation.Retention;\n"); writer.write("import java.lang.annotation.RetentionPolicy;\n"); writer.write("import java.lang.annotation.Target;\n"); writer.write("import net.sf.dobo.Context;\n"); writer.write("import net.sf.dobo.ContextMemberMethod;\n"); writer.write("import net.sf.dobo.ContextMemberType;\n"); writer.write("\n"); writer.write("@Context(" + contextInterface.getName() + ".class)\n"); writer.write("public @interface " + contextName + "{\n"); Method[] methods = contextInterface.getMethods(); for (final Method method : methods) { if (Modifier.isAbstract(method.getModifiers())) { writer.write("\n"); writer.write("\t@Target(ElementType.METHOD)\n"); writer.write("\t@Retention(RetentionPolicy.RUNTIME)\n"); String returnType = method.getReturnType().getName() + ".class"; if (method.getReturnType().isArray()) { returnType = method.getReturnType().getComponentType().getName() + "[].class"; } writer.write("\t@ContextMemberType(" + returnType + ")\n"); StringBuilder builder = new StringBuilder(); Iterator> parameterTypes = Arrays.asList(method.getParameterTypes()) .iterator(); while (parameterTypes.hasNext()) { Class class1 = (Class) parameterTypes.next(); String className = class1.getName() + ".class"; if (class1.isArray()) { className = class1.getComponentType().getName() + "[].class"; } builder.append(className); if (parameterTypes.hasNext()) { builder.append(","); } } writer.write("\t@ContextMemberMethod(name=\"" + method.getName() + "\",parameterType={" + builder.toString() + "})\n"); writer.write("\tpublic @interface " + method.getName() + "{}\n"); } } writer.write("\n"); writer.write("}\n"); } /** * Creating context template * * @param args * @throws ClassNotFoundException * @throws IOException * @throws ClassNotFoundException * @throws IOException */ public static void main(final String[] args) throws ClassNotFoundException, IOException { String contextInterface = ""; String contextOutputPath = "."; boolean createContext = true; while (createContext) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.print("1) Interface class (\"" + contextInterface + "\"): "); String contextInterface_; try { contextInterface_ = reader.readLine(); } catch (final IOException e) { throw new RuntimeException("Cannot read line ", e); } if ((contextInterface_ != null) && !contextInterface_.isEmpty()) { contextInterface = contextInterface_; } System.out.print("2) Context output (\"" + contextOutputPath + "\"): "); String contextOutputPath_ = ""; try { contextOutputPath_ = reader.readLine(); } catch (final IOException e) { e.printStackTrace(); throw new RuntimeException("Cannot read line " + e); } if ((contextOutputPath_ != null) && !contextOutputPath_.isEmpty()) { contextOutputPath = contextOutputPath_; } Class contextInterfaceClass = Class.forName(contextInterface); String contextName = contextInterfaceClass.getSimpleName() + "Context"; System.out.print("3) Context Name (\"" + contextName + "\"): "); String contextName_; try { contextName_ = reader.readLine(); } catch (final IOException e1) { e1.printStackTrace(); throw new RuntimeException("Cannot read line ", e1); } if ((contextName_ != null) && !contextName_.isEmpty()) { contextName = contextName_; } String packageName = contextInterfaceClass.getPackage().getName(); System.out.print("4) Package (\"" + packageName + "\"): "); String packageName_ = reader.readLine(); if ((packageName_ != null) && !packageName_.isEmpty()) { packageName = packageName_; } File file = new File(contextOutputPath + File.separator + contextName + ".java"); if (file.exists()) { if (file.delete()) { System.out.println("File Deleted"); } } else { if (!file.getParentFile().isDirectory()) { try { file.getParentFile().mkdirs(); } catch (final SecurityException se) { se.printStackTrace(); } } } if (file.createNewFile()) { FileWriter writer = new FileWriter(file); generateContext(writer, contextInterfaceClass, packageName, contextName); writer.flush(); System.out.println("New context created (" + file + ")"); System.out.print("would you like create different context (yes) : "); } String createContext_; try { createContext_ = reader.readLine(); if ((createContext_ != null) && !createContext_.isEmpty()) { createContext = Boolean.valueOf(createContext_); } } catch (final IOException e) { e.printStackTrace(); } } } }