/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.apache.vysper.compliance.reporting; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableCollection; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.apache.vysper.compliance.SpecCompliance; import org.apache.vysper.compliance.SpecCompliant; import com.sun.mirror.apt.AnnotationProcessor; import com.sun.mirror.apt.AnnotationProcessorEnvironment; import com.sun.mirror.apt.AnnotationProcessorFactory; import com.sun.mirror.apt.Filer; import com.sun.mirror.declaration.AnnotationTypeDeclaration; import com.sun.mirror.declaration.ClassDeclaration; import com.sun.mirror.declaration.Declaration; import com.sun.mirror.declaration.FieldDeclaration; import com.sun.mirror.declaration.MemberDeclaration; import com.sun.mirror.declaration.MethodDeclaration; import com.sun.mirror.declaration.TypeDeclaration; /** * this generates from all SpecCompliant annotations a HTML doc to show what parts of the spec are covered. * The table is sorted by implemented spec section and links out to the spec and the apidocs. * * apt can be invoked like this (tools.jar must be on the classpath) * apt -cp build/ant/classes;lib/* -s src/main/java -factory org.apache.vysper.compliance.reporting.DocumentSpecCompliantAnnotationFactory * it generates a file spec_compliance.html in src/main/java * * @author The Apache MINA Project (dev@mina.apache.org) */ public class DocumentSpecCompliantAnnotationFactory implements AnnotationProcessorFactory { // Process any set of annotations private static final Collection<String> supportedAnnotations = unmodifiableCollection(Arrays .asList("org.apache.vysper.compliance.*")); // No supported options private static final Collection<String> supportedOptions = emptySet(); public Collection<String> supportedAnnotationTypes() { return supportedAnnotations; } public Collection<String> supportedOptions() { return supportedOptions; } public AnnotationProcessor getProcessorFor(Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) { return new SpecCompliantClassAP(env); } private static class SpecCompliantClassAP implements AnnotationProcessor { protected final AnnotationProcessorEnvironment env; protected AnnotationTypeDeclaration specCompliantAnnotation; protected AnnotationTypeDeclaration specCompliantCollectionAnnotation; protected Map<String, SpecDoc> map = new TreeMap<String, SpecDoc>(); protected PrintWriter fileWriter; SpecCompliantClassAP(AnnotationProcessorEnvironment env) { this.env = env; specCompliantAnnotation = (AnnotationTypeDeclaration) env .getTypeDeclaration("org.apache.vysper.compliance.SpecCompliant"); specCompliantCollectionAnnotation = (AnnotationTypeDeclaration) env .getTypeDeclaration("org.apache.vysper.compliance.SpecCompliance"); try { fileWriter = env.getFiler().createTextFile(Filer.Location.SOURCE_TREE, "", new File("spec_compliance.html"), null); } catch (IOException e) { throw new RuntimeException("could not write to output file", e); } } public void process() { // Retrieve all declarations with SpecCompliant annotations Collection<Declaration> declarations = env.getDeclarationsAnnotatedWith(specCompliantAnnotation); System.out.println("number of solitairy @SpecCompliant: " + declarations.size()); for (Declaration declaration : declarations) { final SpecDoc doc = new SpecDoc(declaration, declaration.getAnnotation(SpecCompliant.class)); map.put(doc.getKey(), doc); } // Retrieve all SpecCompliance-typed declarations and extract SpecCompliant annotations Collection<Declaration> moreDeclarations = env .getDeclarationsAnnotatedWith(specCompliantCollectionAnnotation); System.out.println("number of @SpecCompliance: " + moreDeclarations.size()); for (Declaration declaration : moreDeclarations) { final SpecCompliance compliance = declaration.getAnnotation(SpecCompliance.class); final SpecCompliant[] specCompliants = compliance.compliant(); for (SpecCompliant specCompliant : specCompliants) { if (specCompliant == null) continue; final SpecDoc doc = new SpecDoc(declaration, specCompliant); map.put(doc.getKey(), doc); } } // visit every annotation fromt the now sorted map // write the HTML file fileWriter .println("<html><head>" + "<link rel='stylesheet' type='text/css' href='http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css'>\n" + "<link rel='stylesheet' type='text/css' href='http://yui.yahooapis.com/2.7.0/build/base/base-min.css'>\n" + "</head><body><table><thead><th>spec</th><th>section</th><th>package</th><th>class</th><th>field/method/etc.</th><th>coverage</th><th>implementation</th><th>comment</th></thead>"); for (String key : map.keySet()) { fileWriter.print("<tr>"); System.out.println(key); final SpecDoc doc = map.get(key); // spec fileWriter.print("<td>"); final String specURL = doc.getSpecDocURL(); if (specURL != null) fileWriter.print("<a href='" + specURL + "'>"); final String specDoc = doc.getSpecDoc(); if (specDoc != null) fileWriter.print(specDoc); if (specURL != null) fileWriter.print("</a>"); fileWriter.print("</td>"); // spec section fileWriter.print("<td>"); String specSection = doc.getSpecSection(); if (specSection != null && specSection.length() > 0) { if (specSection.endsWith(".")) specSection = specSection.substring(0, specSection.length() - 1); String anchorLink = Character.isDigit(specSection.charAt(0)) ? "#section-" : "#appendix-"; if (specURL != null) fileWriter.print("<a href='" + specURL + anchorLink + specSection + "'>"); fileWriter.print(specSection); if (specURL != null) fileWriter.print("</a>"); } fileWriter.print("</td>"); // package fileWriter.print("<td>"); final String packageName = doc.getPackage(); if (packageName != null) { fileWriter.print(o_a_v_shortened(packageName)); } fileWriter.print("</td>"); // class fileWriter.print("<td>"); final String className = doc.getClassName(); if (className != null) { fileWriter.print("<a href='" + doc.getFQClassName().replace(".", "/") + ".html'>"); fileWriter.print(o_a_v_cut(packageName, className)); fileWriter.print("</a>"); } fileWriter.print("</td>"); // class element fileWriter.print("<td>"); final String member = doc.getMember(); if (member != null) { if (className != null) { fileWriter.print("<a href='" + className.replace(".", "/") + ".html#" + doc.getMemberAnchor().replace(",", ",%20") + "'>"); } fileWriter.print(o_a_v_cut(packageName, member)); if (className != null) { fileWriter.print("</a>"); } } fileWriter.print("</td>"); // coverage fileWriter.print("<td>"); final SpecCompliant.ComplianceCoverage coverage = doc.getCoverageLevel(); if (coverage != null) fileWriter.print(coverage.toString().toLowerCase()); fileWriter.print("</td>"); // status fileWriter.print("<td>"); final SpecCompliant.ComplianceStatus complianceStatus = doc.getComplianceStatus(); if (complianceStatus != null) fileWriter.print(complianceStatus.toString().toLowerCase()); fileWriter.print("</td>"); // comment fileWriter.print("<td>"); final String comment = doc.getComment(); if (comment != null) fileWriter.print(comment); fileWriter.print("</td>"); fileWriter.println("</tr>"); } fileWriter.println("</table></body>"); } private String o_a_v_shortened(String packageString) { if (packageString != null && packageString.contains("org.apache.vysper.")) return packageString.replace("org.apache.vysper.", "o.a.v."); return packageString; } private String o_a_v_cut(String packageString, String memberString) { if (memberString != null && memberString.contains(packageString + ".")) return memberString.replace(packageString + ".", ""); return memberString; } } static class SpecDoc { Declaration declaration; SpecCompliant specCompliant; SpecDoc(Declaration declaration, SpecCompliant specCompliant) { this.declaration = declaration; this.specCompliant = specCompliant; } public String getClassName() { if (declaration instanceof FieldDeclaration || declaration instanceof MethodDeclaration) { MemberDeclaration memberDeclaration = (MemberDeclaration) declaration; return memberDeclaration.getDeclaringType().getQualifiedName(); } else { return declaration.getSimpleName(); } } public String getFQClassName() { if (declaration instanceof FieldDeclaration || declaration instanceof MethodDeclaration) { MemberDeclaration memberDeclaration = (MemberDeclaration) declaration; return memberDeclaration.getDeclaringType().getQualifiedName(); } else if (declaration instanceof ClassDeclaration) { ClassDeclaration classDeclaration = (ClassDeclaration) declaration; return classDeclaration.getPackage() + "." + classDeclaration.getSimpleName(); } else { return declaration.getSimpleName(); } } public String getPackage() { if (declaration instanceof TypeDeclaration) { TypeDeclaration typeDeclaration = (TypeDeclaration) declaration; return typeDeclaration.getPackage().getQualifiedName(); } else if (declaration instanceof FieldDeclaration || declaration instanceof MethodDeclaration) { MemberDeclaration memberDeclaration = (MemberDeclaration) declaration; return memberDeclaration.getDeclaringType().getPackage().getQualifiedName(); } else { return null; } } public String getMember() { if (declaration instanceof FieldDeclaration || declaration instanceof MethodDeclaration) { MemberDeclaration memberDeclaration = (MemberDeclaration) declaration; return declaration.getSimpleName(); } else { return null; } } public String getMemberAnchor() { if (declaration instanceof FieldDeclaration || declaration instanceof MethodDeclaration) { MemberDeclaration memberDeclaration = (MemberDeclaration) declaration; return declaration.toString(); } else { return null; } } public String getKey() { return getSpecDoc() + " " + getSpecSection() + " " + getClassName() + " " + getMember() + " " + getCoverageLevel() + " " + getComplianceStatus(); } public String getSpecSection() { return specCompliant.section(); } public String getSpecDoc() { final String specRaw = specCompliant.spec(); if (specRaw == null) return null; return specRaw.toLowerCase(); } public String getSpecDocURL() { final String spec = getSpecDoc(); if (spec == null) return null; if (spec.startsWith("xep")) return "http://xmpp.org/extensions/" + spec + ".html"; if (spec.startsWith("rfc")) { if (!spec.contains("bis")) { return "http://tools.ietf.org/html/" + spec; } else { return "http://tools.ietf.org/html/draft-saintandre-" + spec; } } return null; } public SpecCompliant.ComplianceCoverage getCoverageLevel() { return specCompliant.coverage(); } public SpecCompliant.ComplianceStatus getComplianceStatus() { return specCompliant.status(); } public String getComment() { return specCompliant.comment(); } } }