package com.farata.cdb.annotations.processor; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import clear.cdb.extjs.annotations.JSFillChildrenMethod; import clear.cdb.extjs.annotations.JSGetMethod; import clear.cdb.extjs.annotations.JSJPQLMethod; import clear.cdb.extjs.annotations.JSService; import clear.cdb.extjs.annotations.JSTransferInfo; import clear.cdb.extjs.annotations.JSUpdateInfo; import clear.cdb.extjs.annotations.DEFAULT; import com.farata.cdb.annotations.helper.AnnotationsHelper; import com.farata.cdb.annotations.helper.HQLHelper; import com.sun.mirror.apt.AnnotationProcessor; import com.sun.mirror.apt.AnnotationProcessorEnvironment; import com.sun.mirror.apt.AnnotationProcessorFactory; import com.sun.mirror.declaration.AnnotationMirror; import com.sun.mirror.declaration.AnnotationTypeDeclaration; import com.sun.mirror.declaration.AnnotationTypeElementDeclaration; import com.sun.mirror.declaration.AnnotationValue; import com.sun.mirror.declaration.MethodDeclaration; import com.sun.mirror.declaration.ParameterDeclaration; import com.sun.mirror.declaration.TypeDeclaration; import com.sun.mirror.type.MirroredTypeException; import com.sun.mirror.type.ReferenceType; import com.sun.mirror.type.TypeMirror; public class CDBFullAnnotationProcessor implements AnnotationProcessor, AnnotationProcessorFactory { public static final String OPTION_CDB_ANNOTATIONS_FILE = "-Acom.faratasystems.cdb.annotations.file"; private static final String BR = "\n"; private Set<AnnotationTypeDeclaration> annotationTypeDeclarations; private AnnotationProcessorEnvironment annotationProcessorEnvironment; @Override public void process() { StringBuffer processResult = new StringBuffer(); Collection<TypeDeclaration> specifiedTypeDeclarations = annotationProcessorEnvironment .getTypeDeclarations(); processResult.append("<annotated-types>\n"); HashMap<String, String> dtoToEntityMappings = new HashMap<String, String>(); for (TypeDeclaration specifiedTypeDeclaration : specifiedTypeDeclarations) { JSService serv = specifiedTypeDeclaration .getAnnotation(JSService.class); try { if (serv != null) { processTypeDeclaration(specifiedTypeDeclaration, processResult, "\t"); createEntityToDTOMappings(specifiedTypeDeclaration, dtoToEntityMappings); } } catch (Exception e) { e.printStackTrace(); } } processResult.append("\t<dto-mappings>" + BR); for (String dtoName : dtoToEntityMappings.keySet()) { processResult.append("\t\t<dto-mapping dto-name=\"" + dtoName + "\" entity-name=\"" + dtoToEntityMappings.get(dtoName) + "\"/>" + BR); } processResult.append("\t</dto-mappings>" + BR); processResult.append("</annotated-types>\n"); String outputFile = getOption(OPTION_CDB_ANNOTATIONS_FILE); outputFile = CDBHQLAnnotationProcessor.resolveFile(outputFile); writeContent(processResult.toString(), outputFile); } private void processTypeDeclaration(TypeDeclaration typeDeclaration, StringBuffer processResult, String indent) throws Exception { processResult.append(indent + "<annotated-type name=\"" + typeDeclaration.getQualifiedName() + "\">" + BR); Collection<AnnotationMirror> annotationsCollection = typeDeclaration .getAnnotationMirrors(); processResult.append(indent + "\t<annotations>" + BR); for (AnnotationMirror annotationMirror : annotationsCollection) { processAnnotation(annotationMirror, processResult, indent + "\t\t"); } processResult.append(indent + "\t</annotations>" + BR); Collection<? extends MethodDeclaration> methodsCollection = typeDeclaration .getMethods(); processResult.append(indent + "\t<methods>" + BR); for (MethodDeclaration methodDeclaration : methodsCollection) { processMethodDeclaration(methodDeclaration, processResult, indent + "\t\t"); } processResult.append(indent + "\t</methods>" + BR); processResult.append(indent + "</annotated-type>" + BR); } private HashMap<String, String> createEntityToDTOMappings( TypeDeclaration typeDeclaration, HashMap<String, String> dtoToEntityMappings) { Collection<? extends MethodDeclaration> methods = typeDeclaration .getMethods(); for (MethodDeclaration method : methods) { JSJPQLMethod jpqlMethod = method .getAnnotation(JSJPQLMethod.class); Class<?> entity = null; if (jpqlMethod != null) { // Get from transferInfo JSTransferInfo transferInfo = jpqlMethod.transferInfo(); try { transferInfo.mappedBy(); } catch (MirroredTypeException e) { Class<?> mappedBy = extractClass(e); if (!mappedBy.equals(DEFAULT.class)) { entity = mappedBy; } } } else { JSGetMethod getMethod = method .getAnnotation(JSGetMethod.class); if (getMethod != null) { JSTransferInfo transferInfo = getMethod.transferInfo(); try { transferInfo.mappedBy(); } catch (MirroredTypeException e) { Class<?> mappedBy = extractClass(e); if (!mappedBy.equals(DEFAULT.class)) { entity = mappedBy; } } } else { JSFillChildrenMethod fillChildrenMethod = method .getAnnotation(JSFillChildrenMethod.class); if (fillChildrenMethod != null) { JSTransferInfo transferInfo = fillChildrenMethod .transferInfo(); try { transferInfo.mappedBy(); } catch (MirroredTypeException e) { Class<?> mappedBy = extractClass(e); if (!mappedBy.equals(DEFAULT.class)) { entity = mappedBy; } } } } } if (entity != null) { String transferType = getMethodTransferType(method); if (transferType == null || transferType.equals("")) { continue; } String canonicalName = entity.getCanonicalName(); if (!transferType.equals(canonicalName)) { String cn = dtoToEntityMappings.get(transferType); if (cn != null && !cn.equals(canonicalName)) { System.err.println("WARNING: '" + transferType + "' is already mapped by '" + cn + "' in '" + typeDeclaration.getQualifiedName() + "'"); continue; } String tf = findDtoByEntity(dtoToEntityMappings, canonicalName); if (tf != null && !tf.equals(transferType)) { System.err.println("WARNING: '" + tf + "' is already mapped by '" + canonicalName + "' in '" + typeDeclaration.getQualifiedName() + "'"); continue; } dtoToEntityMappings.put(transferType, canonicalName); } } } return dtoToEntityMappings; } private String findDtoByEntity(HashMap<String, String> dtoToEntityMappings, String name) { for (String key : dtoToEntityMappings.keySet()) { if (dtoToEntityMappings.get(key).equals(name)) { return key; } } return null; } private Class<?> extractClass(MirroredTypeException e) { String type = e.toString(); type = type.substring(type.lastIndexOf(" ") + 1, type.length()).trim(); Class<?> mappedBy = null; try { mappedBy = getClass().getClassLoader().loadClass(type); } catch (ClassNotFoundException e1) { e1.printStackTrace(); } return mappedBy; } private String getMethodTransferType(MethodDeclaration methodDeclaration) { // Check return type parameter TypeMirror type = methodDeclaration.getReturnType(); String s = type.toString(); String transferType = AnnotationsHelper.getTypeParameter(s); if (transferType != null && transferType.length() > 0 && !transferType.equals("?")) { return transferType; } // Check transferType annotation JSJPQLMethod jpqlMethod = methodDeclaration .getAnnotation(JSJPQLMethod.class); if (jpqlMethod != null) { transferType = jpqlMethod.transferInfo().type(); if (transferType != null && transferType.length() > 0 && !transferType.equals("?")) { return transferType; } // transferType = jpqlMethod.transferType(); // if (transferType != null && transferType.length() > 0 // && !transferType.equals("?")) { // return transferType; // } } JSGetMethod getMethod = methodDeclaration .getAnnotation(JSGetMethod.class); if (getMethod != null) { transferType = getMethod.transferInfo().type(); if (transferType != null && transferType.length() > 0 && !transferType.equals("?")) { return transferType; } } JSFillChildrenMethod fillChildrenMethod = methodDeclaration .getAnnotation(JSFillChildrenMethod.class); if (fillChildrenMethod != null) { transferType = fillChildrenMethod.transferInfo().type(); if (transferType != null && transferType.length() > 0 && !transferType.equals("?")) { return transferType; } } // Check updateEntity annotation JSUpdateInfo uiAnnotation = methodDeclaration .getAnnotation(JSUpdateInfo.class); if (uiAnnotation != null) { Class<?> updateEntity = null; try { updateEntity = uiAnnotation.updateEntity(); } catch (MirroredTypeException e) { updateEntity = extractClass(e); } if (updateEntity != null) { transferType = updateEntity.getCanonicalName(); if (transferType != null && transferType.length() > 0 && !transferType.equals("?")) { return transferType; } } } return null; } private void processAnnotation(AnnotationMirror annotation, StringBuffer processResult, String indent) throws Exception { getAnnotationValues(annotation, processResult, indent); } private void processMethodDeclaration(MethodDeclaration methodDeclaration, StringBuffer processResult, String indent) throws Exception { processResult.append(indent + "<method to-string=\"" + methodToString(methodDeclaration).replaceAll("<", "<") .replaceAll(">", ">") + "\" name=\"" + methodDeclaration.getSimpleName() + "\">\n"); Collection<ParameterDeclaration> params = methodDeclaration .getParameters(); processResult.append(indent + "\t<parameters>\n"); int count = 0; for (ParameterDeclaration param : params) { processResult.append(indent + "\t\t<parameter name=\"" + param.getSimpleName() + "\" type=\"" + getTypeName(param) + "\" order=\"" + count + "\"/>\n"); count++; } processResult.append(indent + "\t</parameters>\n"); Collection<AnnotationMirror> annotationMirrors = methodDeclaration .getAnnotationMirrors(); processResult.append(indent + "\t<annotations>\n"); for (AnnotationMirror annotationMirror : annotationMirrors) { processAnnotation(annotationMirror, processResult, indent + "\t\t"); } processResult.append(indent + "\t</annotations>\n"); processResult.append(indent + "</method>\n"); } private String getTypeName(ParameterDeclaration param) { String paramType = param.getType().toString(); paramType = paramType.replace("<", "<"); paramType = paramType.replace(">", ">"); return paramType; } private static String methodToString(MethodDeclaration method) { StringBuilder sb = new StringBuilder(); TypeMirror genRetType = method.getReturnType(); sb.append((genRetType.toString()) + " "); sb.append(method.getSimpleName() + "("); Collection<ParameterDeclaration> params = method.getParameters(); boolean first = true; for (ParameterDeclaration param : params) { if (first) { sb.append(param.toString()); first = false; } else { sb.append(", "); sb.append(param.toString()); } } sb.append(")"); ReferenceType[] exceptions = method.getThrownTypes().toArray( new ReferenceType[0]); if (exceptions.length > 0) { sb.append(" throws "); for (int k = 0; k < exceptions.length; k++) { sb.append(exceptions[k].toString()); if (k < (exceptions.length - 1)) sb.append(", "); } } return sb.toString(); } public void getAnnotationValues(AnnotationMirror annotation, StringBuffer out, String indent) throws Exception { String annotationName = annotation == null ? "" : annotation .getAnnotationType().getDeclaration().getQualifiedName(); out.append(indent + "<annotation name=\"" + annotationName + "\">\n"); if (annotation != null) { out.append(indent + "\t<exists/>\n"); Map<AnnotationTypeElementDeclaration, AnnotationValue> elementValues = annotation .getElementValues(); for (AnnotationTypeElementDeclaration annotationMethod : elementValues .keySet()) { Object res = elementValues.get(annotationMethod).getValue(); if (res instanceof AnnotationMirror) { out.append(indent + "\t<method name=\"" + annotationMethod.getSimpleName() + "\">\n" + indent + "\t\t<value>\n"); AnnotationMirror ann = (AnnotationMirror) res; getAnnotationValues(ann, out, indent + "\t\t\t"); out.append(indent + "\t\t</value>\n"); out.append(indent + "\t</method>\n"); } else { if ("query".equals(annotationMethod.getSimpleName())) { if (HQLHelper.factory == null) { String configurationFile = CDBHQLAnnotationProcessor.resolveFile(getOption(CDBHQLAnnotationProcessor.OPTION_CDB_CONFIGURATION_FILE)); HQLHelper.createSessionFactory(configurationFile); } String query = AnnotationsHelper.adjustQuery(res .toString()); String hqlReturnTypes = AnnotationsHelper .getHQLReturnTypes(query, indent + "\t\t"); out.append(indent + "\t<method name=\"" + annotationMethod.getSimpleName() + "\" value=\"" + query + "\">\n"); out.append(hqlReturnTypes); out.append(indent + "\t</method>\n"); } else { out.append(indent + "\t<method name=\"" + annotationMethod.getSimpleName() + "\" value=\"" + res.toString() + "\"/>\n"); } } } } out.append(indent + "</annotation>\n"); } private void writeContent(String sOut, String outputFile) { try { FileWriter fw = new FileWriter(outputFile); fw.write(sOut); fw.close(); } catch (IOException e) { System.out.println("WARNING: " + e); } } private static String readContent(File file) { FileReader fr = null; try { fr = new FileReader(file); char[] b = new char[1024]; int off = 0; int len = b.length; StringBuffer sb = new StringBuffer(); do { len = fr.read(b, off, len); if (len != -1) { sb.append(new String(b, off, len)); } } while (len != -1); return sb.toString(); } catch (Throwable e) { System.out.println("WARNING: " + e); } finally { if (fr != null) { try { fr.close(); } catch (IOException e) { } } } return null; } protected String getOption(String name) { for (String option : this.annotationProcessorEnvironment.getOptions() .keySet()) { if (option.contains(name)) { String[] ret = option.split("="); if (ret.length > 1) { return ret[1]; } else { return this.annotationProcessorEnvironment.getOptions() .get(option); } } } return null; } @Override public AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> annotationTypeDeclarations, AnnotationProcessorEnvironment annotationProcessorEnvironment) { this.annotationTypeDeclarations = annotationTypeDeclarations; this.annotationProcessorEnvironment = annotationProcessorEnvironment; return this; } @Override public Collection<String> supportedAnnotationTypes() { ArrayList<String> result = new ArrayList<String>(); result.add("*"); return result; } @Override public Collection<String> supportedOptions() { return null; } }