/* * Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved. */ package jsystem.extensions.report.html; import java.io.File; import java.io.FileNotFoundException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Method; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import jsystem.framework.FrameworkOptions; import jsystem.framework.JSystemProperties; import jsystem.runner.loader.LoadersManager; import jsystem.utils.FileUtils; import com.thoughtworks.qdox.JavaDocBuilder; import com.thoughtworks.qdox.model.DocletTag; import com.thoughtworks.qdox.model.JavaClass; import com.thoughtworks.qdox.model.JavaMethod; /** * This class give general code base services. * To get the service use the static method: <code>HtmlCodeWriter.getInstance()</code>. Use <code>init</code> to reset.<p> * Following code services:<br> * 1. getClassJavaDoc - return the class documentation.<br> * 2. getMethodJavaDoc - return the method documentation.<br> * 3. getMethodAnnotation - return the method doclet tag.<br> * 4. getCode - take tests sources and convert it to html and present it as * part of the report. the attribute 'tests.src' is used to find whare is the * tests sources located.<br> * * @author guy.arieli * */ public class HtmlCodeWriter { private static Logger log = Logger.getLogger(HtmlCodeWriter.class.getName()); private static HtmlCodeWriter writer; public static HtmlCodeWriter getInstance() { if (writer == null) { writer = new HtmlCodeWriter(); } return writer; } public static void init() { if(writer != null){ writer.close(); } writer = null; } /** * Contain all the sources to parse. */ JavaDocBuilder docBuilder = null; File srcDir; /** * Hold all the files that were loaded to the doc builder and the last time * they were modified. * Only updated file will be loaded. */ HashMap<File, Long> filesTime; private HtmlCodeWriter() { /* * Find the source folder */ String testsSourceFolder = JSystemProperties.getInstance().getPreference(FrameworkOptions.TESTS_SOURCE_FOLDER); if (testsSourceFolder == null) { String testsClassFolderName = null; try { testsClassFolderName = JSystemProperties.getCurrentTestsPath(); } catch (Exception e1) { // can't find the current test pass log.log(Level.WARNING,"Failed to get current tests path"); } if (testsClassFolderName != null) { File testsClassFolder = new File(testsClassFolderName); if (new File(testsClassFolder.getParent(),"tests").exists()){ //We are in a Ant structured project testsSourceFolder = (new File(testsClassFolder.getParent(), "tests")).getPath(); JSystemProperties.getInstance().setPreference(FrameworkOptions.TESTS_SOURCE_FOLDER, testsSourceFolder); JSystemProperties.getInstance().setPreference(FrameworkOptions.RESOURCES_SOURCE_FOLDER, testsSourceFolder); }else { //We are in a Maven structured project testsSourceFolder = (new File(testsClassFolder.getParentFile().getParentFile(), "src/main/java")).getPath(); JSystemProperties.getInstance().setPreference(FrameworkOptions.TESTS_SOURCE_FOLDER, testsSourceFolder); String resourcesSourceFolder = (new File(testsClassFolder.getParentFile().getParentFile(), "src/main/resources")).getPath(); JSystemProperties.getInstance().setPreference(FrameworkOptions.RESOURCES_SOURCE_FOLDER, resourcesSourceFolder); } } else { testsSourceFolder = System.getProperty("user.dir"); } } srcDir = new File(testsSourceFolder); docBuilder = new JavaDocBuilder(); filesTime = new HashMap<File, Long>(); } public void close (){ docBuilder = null; filesTime = null; } /** * Get the test code formated as HTML. * @param className the test class name. * @return an html with the code formated. * @throws Exception when java2html class is missing, the file is not found or other error occurs */ public String getCode(String className) throws FileNotFoundException, ClassNotFoundException, Exception { File srcFile = new File(srcDir.getPath(), className.replace('.', File.separatorChar) + ".java"); if (!srcFile.exists()) { srcFile = new File(srcDir.getPath(), className.replace('.', File.separatorChar) + ".groovy"); if (!srcFile.exists()) { throw new FileNotFoundException(srcFile.getPath()); } } // Create a reader of the raw input text // Parse the raw text to a JavaSource object Class<?> sourceParserClass = LoadersManager.getInstance().getLoader().loadClass("de.java2html.javasource.JavaSourceParser"); Object sourceParser = sourceParserClass.newInstance(); Method parseMethod = sourceParserClass.getMethod("parse", File.class); if (parseMethod == null) { return ""; } Object source = parseMethod.invoke(sourceParser, srcFile); if (source == null) { return ""; } Class<?> converterClass = LoadersManager.getInstance().getLoader().loadClass("de.java2html.converter.JavaSource2HTMLConverter"); StringWriter writer = new StringWriter(); Object converter = converterClass.getConstructor(source.getClass()).newInstance(source); converterClass.getMethod("convert", Writer.class).invoke(converter, writer); // JavaSource source = null; // source = new JavaSourceParser().parse(srcFile); // Create a converter and write the JavaSource object as Html // JavaSource2HTMLConverter converter = new JavaSource2HTMLConverter(source); // StringWriter writer = new StringWriter(); // converter.convert(writer); String toReturn = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" + "<html><head>\n" + // "<title></title>\n" + "</head>\n" + "<body>\n" + writer.toString() + "</body>\n" + "</html>\n"; return toReturn; } /** * Get the class javadoc * @param className the class name * @return the class javadoc or null if not exist or not found * @throws Exception */ public String getClassJavaDoc(String className) throws Exception { processSource(className); JavaClass cls = docBuilder.getClassByName(className); if(cls == null){ return null; } return cls.getComment(); } /** * get the method javadoc * @param className the class name to look for * @param methodName the method to look for * @return the documenation of the method or null if not exist * @throws Exception */ public String getMethodJavaDoc(String className, String methodName) throws Exception { processSource(className); JavaClass cls = docBuilder.getClassByName(className); if(cls == null){ return null; } JavaMethod[] methods = cls.getMethods(); for(JavaMethod method: methods){ if(method.getName().equals(methodName)){ return method.getComment(); } } return null; } /** * Get the doclet tag for a specifc class and method * @param className the class to look for. * @param methodName the method to look for. * @param annotation the doclet to look for. * @return the doclet if exist of null if not. */ public String getMethodAnnotation(String className, String methodName, String annotation){ processSource(className); JavaClass cls = docBuilder.getClassByName(className); if(cls == null){ return null; } JavaMethod[] methods = cls.getMethods(); for(JavaMethod method: methods){ if(method.getName().equals(methodName)){ DocletTag tag = method.getTagByName(annotation); if(tag == null){ return null; } return tag.getValue(); } } // method not found Class<?> c; try { c = LoadersManager.getInstance().getLoader().loadClass(className); } catch (ClassNotFoundException e) { return null; } if(c != null && !c.equals(Object.class)){ return getMethodAnnotation(c.getSuperclass().getName(), methodName, annotation); } return null; } /** * Process the class and reload it if it changed. * @param className * @throws Exception */ private void processSource(String className){ File testSrc = new File(srcDir, className.replace('.', File.separatorChar) + ".java"); if(!testSrc.exists()){ // if the file doesn't exist return //Added support for Groovy tests testSrc = new File(srcDir, className.replace('.', File.separatorChar) + ".groovy"); if(!testSrc.exists()){ return; } } /* * Check if the last modified time changed * if not return */ Long time = filesTime.get(testSrc); if(time == null || !time.equals(testSrc.lastModified())){ // not exist or changed try { docBuilder.addSource(preProcessCode(FileUtils.read(testSrc))); } catch (Throwable e) { // ignore process fail log.log(Level.FINE, "Fail to process file: " + testSrc.getAbsolutePath(), e); } filesTime.put(testSrc, testSrc.lastModified()); } } /** * Remove enumeration definition * @param code the original code * @return Reader from the changed code */ public static Reader preProcessCode(String code){ return new StringReader(code); } }