Package org.codehaus.backport175.compiler

Source Code of org.codehaus.backport175.compiler.AnnotationC

/*******************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved.                      *
* http://backport175.codehaus.org                                                         *
* --------------------------------------------------------------------------------------- *
* The software in this package is published under the terms of Apache License Version 2.0 *
* a copy of which has been included with this distribution in the license.txt file.       *
*******************************************************************************************/
package org.codehaus.backport175.compiler;

import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.DocletTag;

import org.codehaus.backport175.compiler.bytecode.AnnotationEnhancer;
import org.codehaus.backport175.compiler.javadoc.JavaDocParser;
import org.codehaus.backport175.compiler.javadoc.RawAnnotation;
import org.codehaus.backport175.compiler.javadoc.SourceParseException;
import org.codehaus.backport175.compiler.parser.ParseException;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
* <p/>Annotation compiler. <p/>Extracts the annotations from JavaDoc tags and inserts them into the bytecode of the
* class.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
*/
public class AnnotationC {
    private static final String COMMAND_LINE_OPTION_DASH = "-";
    private static final String COMMAND_LINE_OPTION_VERBOSE = "-verbose";
    private static final String COMMAND_LINE_OPTION_CUSTOM = "-config";
    private static final String COMMAND_LINE_OPTION_SRC = "-src";
    private static final String COMMAND_LINE_OPTION_SRCFILES = "-srcfiles";
    private static final String COMMAND_LINE_OPTION_SRCINCLUDES = "-srcincludes";
    private static final String COMMAND_LINE_OPTION_CLASSES = "-classes";
    private static final String COMMAND_LINE_OPTION_DEST = "-dest";

    private static final String FILE_SEPARATOR = File.separator;

    /**
     * Compilation event handler
     */
    private final MessageHandler m_handler;

    /**
     * The class loader
     */
    private final ClassLoader m_loader;

    /**
     * The parser for src files
     */
    private final JavaDocParser m_javaDocParser;

    /**
     * The annotation repository
     */
    private final AnnotationInterfaceRepository m_repository;

    /**
     * Creates a new AnnotationC compiler instance.
     *
     * @param loader
     * @param parser
     * @param repository
     * @param handler
     */
    private AnnotationC(
            final ClassLoader loader,
            final JavaDocParser parser,
            final AnnotationInterfaceRepository repository,
            final MessageHandler handler) {
        m_loader = loader;
        m_javaDocParser = parser;
        m_repository = repository;
        m_handler = handler;
    }

    /**
     * Runs the compiler from the command line.
     *
     * @param args
     */
    public static void main(String[] args) {
        if (args.length < 4) {
            printUsage();
        }
        Map commandLineOptions = parseCommandLineOptions(args);

        String propertiesFilesPath = (String)commandLineOptions.get(COMMAND_LINE_OPTION_CUSTOM);
        List propertiesFilesList = new ArrayList();
        if (propertiesFilesPath != null) {
            StringTokenizer st = new StringTokenizer(propertiesFilesPath, File.pathSeparator);
            while (st.hasMoreTokens()) {
                propertiesFilesList.add(st.nextToken());
            }
        }
        String[] propertiesFiles = (String[])propertiesFilesList.toArray(new String[0]);

        compile(
                "true".equals(commandLineOptions.get(COMMAND_LINE_OPTION_VERBOSE)),
                (String)commandLineOptions.get(COMMAND_LINE_OPTION_SRC),
                (String)commandLineOptions.get(COMMAND_LINE_OPTION_SRCFILES),
                (String)commandLineOptions.get(COMMAND_LINE_OPTION_SRCINCLUDES),
                (String)commandLineOptions.get(COMMAND_LINE_OPTION_CLASSES),
                (String)commandLineOptions.get(COMMAND_LINE_OPTION_DEST),
                propertiesFiles
        );
    }

    /**
     * Compiles the annotations, called from the main method.
     *
     * @param verbose
     * @param srcDirList
     * @param srcFileList
     * @param classPath
     * @param destDir
     * @param annotationPropetiesFiles
     */
    private static void compile(
            final boolean verbose,
            final String srcDirList,
            final String srcFileList,
            final String srcFileIncludes,
            final String classPath,
            String destDir,
            final String[] annotationPropetiesFiles) {
        if (srcDirList == null && srcFileList == null && srcFileIncludes == null) {
            throw new IllegalArgumentException("one of src or srcfiles or srcincludes must be not null");
        }
        if ((srcDirList != null && srcFileList != null) ||
            (srcDirList != null && srcFileIncludes != null) ||
            (srcFileList != null && srcFileIncludes != null)) {
            throw new IllegalArgumentException("maximum one of src, srcfiles or srcincludes must be not null");
        }
        if (classPath == null) {
            throw new IllegalArgumentException("class path can not be null");
        }
        if (destDir == null) {
            destDir = classPath;
        }

        String[] srcDirs = new String[0];
        String[] srcFiles = new String[0];
        if (srcDirList != null) {
            srcDirs = split(srcDirList, File.pathSeparator);
        } else if (srcFileList != null) {
            srcFiles = split(srcFileList, FILE_SEPARATOR);
        } else {
            srcFiles = loadSourceList(srcFileIncludes);
        }

        compile(
                srcDirs,
                srcFiles,
                split(classPath, File.pathSeparator),
                destDir,
                annotationPropetiesFiles,
                new MessageHandler.PrintWriter(verbose)
        );
    }

    /**
     * Compiles the annotations.
     *
     * @param srcDirs
     * @param srcFiles
     * @param classpath
     * @param destDir
     * @param annotationPropertiesFiles
     * @param messageHandler
     */
    public static void compile(
            final String[] srcDirs,
            final String[] srcFiles,
            final String[] classpath,
            final String destDir,
            final String[] annotationPropertiesFiles,
            final MessageHandler messageHandler) {

        URL[] classPath = new URL[classpath.length];
        final ClassLoader compilationLoader;
        try {
            for (int i = 0; i < classpath.length; i++) {
                classPath[i] = new File(classpath[i]).toURL();
            }
            compilationLoader = new URLClassLoader(classPath, AnnotationC.class.getClassLoader());
        } catch (MalformedURLException e) {
            String message = "URL [" + classPath + "] is not valid: " + e.toString();
            messageHandler.error(new CompilerException(message, e));
            return;
        }

        String destDirToUse = destDir;
        if (destDir == null) {
            if (classpath.length != 1) {
                messageHandler.error(new CompilerException("destDir must be specified since classpath is composite"));
                return;
            }
            destDirToUse = classpath[0];
        }

        // set up the parser sources
        final JavaDocParser javaDocParser = new JavaDocParser();
        try {
            // add classloader
            javaDocParser.addClassLoaderToSearchPath(compilationLoader);

            // add src dirs
            StringBuffer logDirs = new StringBuffer("parsing source dirs:");
            for (int i = 0; i < srcDirs.length; i++) {
                logDirs.append("\n\t" + srcDirs[i]);
            }
            messageHandler.info(logDirs.toString());
            javaDocParser.addSourceTrees(srcDirs);

            // add src files
            logDirs = new StringBuffer();
            for (int i = 0; i < srcFiles.length; i++) {
                logDirs.append("\n\t" + srcFiles[i]);
                javaDocParser.addSource(srcFiles[i]);
            }
            if (srcFiles.length > 0) {
                messageHandler.info(logDirs.toString());
            }

            final AnnotationInterfaceRepository repository = new AnnotationInterfaceRepository(messageHandler);
            repository.registerPropertiesFiles(annotationPropertiesFiles, compilationLoader);

            final AnnotationC compiler = new AnnotationC(compilationLoader, javaDocParser, repository, messageHandler);

            // do the actual compile
            compiler.doCompile(classPath, destDirToUse);

        } catch (SourceParseException e) {
            messageHandler.error(e);
            return;
        } catch (CompilerException e) {
            messageHandler.error(e);
            return;
        } catch (Throwable t) {
            messageHandler.error(new CompilerException("unexpected exception: " + t.toString(), t));
            return;
        }
    }

    /**
     * Compiles the annotations.
     *
     * @param classPath
     * @param destDir
     */
    private void doCompile(final URL[] classPath, final String destDir) {
        logInfo("compiling annotations...");

        // get all the classes
        JavaClass[] classes = m_javaDocParser.getJavaClasses();
        for (int i = 0; i < classes.length; i++) {
            JavaClass clazz = classes[i];
            logInfo("parsing class [" + clazz.getFullyQualifiedName() + ']');
            try {
                AnnotationEnhancer enhancer = new AnnotationEnhancer(m_handler);
                if (enhancer.initialize(clazz.getFullyQualifiedName(), classPath)) {
                    handleClassAnnotations(enhancer, clazz);
                    //handleInnerClassAnnotations(enhancer, clazz, destDir);
                    JavaMethod[] methods = clazz.getMethods();
                    for (int j = 0; j < methods.length; j++) {
                        JavaMethod method = methods[j];
                        if (method.isConstructor()) {
                            handleConstructorAnnotations(enhancer, method);
                        } else {
                            handleMethodAnnotations(enhancer, method);
                        }
                    }
                    JavaField[] fields = clazz.getFields();
                    for (int j = 0; j < fields.length; j++) {
                        handleFieldAnnotations(enhancer, fields[j]);
                    }

                    // write enhanced class to disk
                    enhancer.write(destDir);
                }
            } catch (ParseException pe) {
                m_handler.error(pe);
                // non critical, go on
            } catch (CompilerException ce) {
                m_handler.error(ce);
                return;
            } catch (Throwable t) {
                t.printStackTrace();
                m_handler.error(
                        new CompilerException(
                                "could not compile annotations for class ["//FIXME location
                                + clazz.getFullyQualifiedName() +
                                "] due to: " +
                                t.toString()
                        )
                );
                return;
            }
        }
        logInfo("compiled classes written to " + destDir);
        logInfo("compilation successful");
    }

    /**
     * Handles the class annotations.
     *
     * @param enhancer
     * @param clazz
     */
    private void handleClassAnnotations(final AnnotationEnhancer enhancer, final JavaClass clazz) {
        DocletTag[] tags = clazz.getTags();
        for (int i = 0; i < tags.length; i++) {
            RawAnnotation rawAnnotation = getRawAnnotation(
                    tags[i], enhancer.getClassName(), enhancer.getClassFileName()
            );
            if (rawAnnotation == null) {
                continue;
            }
            enhancer.insertClassAnnotation(rawAnnotation);
            logInfo(
                    "\tprocessing class annotation [" + rawAnnotation.getName() + " @ "
                    + clazz.getFullyQualifiedName() + ']'
            );
        }
    }

    /**
     * Handles the method annotations.
     *
     * @param enhancer
     * @param method
     */
    private void handleMethodAnnotations(final AnnotationEnhancer enhancer, final JavaMethod method) {
        DocletTag[] tags = method.getTags();
        for (int i = 0; i < tags.length; i++) {
            RawAnnotation rawAnnotation = getRawAnnotation(
                    tags[i], enhancer.getClassName(), enhancer.getClassFileName()
            );
            if (rawAnnotation == null) {
                continue;
            }
            enhancer.insertMethodAnnotation(method, rawAnnotation);
            logInfo(
                    "\tprocessing method annotation [" + rawAnnotation.getName() + " @ "
                    + method.getParentClass().getName() + '.' +
                    method.getName()
                    + ']'
            );
        }
    }

    /**
     * Handles the constructor annotations.
     *
     * @param enhancer
     * @param constructor
     */
    private void handleConstructorAnnotations(final AnnotationEnhancer enhancer, final JavaMethod constructor) {
        DocletTag[] tags = constructor.getTags();
        for (int i = 0; i < tags.length; i++) {
            RawAnnotation rawAnnotation = getRawAnnotation(
                    tags[i], enhancer.getClassName(), enhancer.getClassFileName()
            );
            if (rawAnnotation == null) {
                continue;
            }
            enhancer.insertConstructorAnnotation(constructor, rawAnnotation);
            logInfo(
                    "\tprocessing constructor annotation [" + rawAnnotation.getName() + " @ "
                    + constructor.getParentClass().getName() + '.' +
                    constructor.getName()
                    + ']'
            );
        }
    }

    /**
     * Handles the field annotations.
     *
     * @param enhancer
     * @param field
     */
    private void handleFieldAnnotations(final AnnotationEnhancer enhancer, final JavaField field) {
        DocletTag[] tags = field.getTags();
        for (int i = 0; i < tags.length; i++) {
            RawAnnotation rawAnnotation = getRawAnnotation(
                    tags[i], enhancer.getClassName(), enhancer.getClassFileName()
            );
            if (rawAnnotation == null) {
                continue;
            }
            enhancer.insertFieldAnnotation(field, rawAnnotation);
            logInfo("\tprocessing field annotation [" + rawAnnotation.getName() + " @ " + field.getName() + ']');
        }
    }

    /**
     * Processes the doclet tags and creates a raw annotation to use for further processing.
     *
     * @param tag                    the doclet tag
     * @param enclosingClassName
     * @param enclosingClassFileName
     * @return the raw annotation data
     */
    private RawAnnotation getRawAnnotation(
            final DocletTag tag, final String enclosingClassName, final String enclosingClassFileName) {
        String annotationName = tag.getName();
        int index = annotationName.indexOf('(');
        if (index != -1) {
            annotationName = annotationName.substring(0, index);
        }

        Class annotationInterface = m_repository.getAnnotationInterfaceFor(annotationName, m_loader);
        if (annotationInterface == null) {
            // not found, and the AnnotationInterfaceRepository.ANNOTATION_IGNORED has been populated
            //FIXME - raise what ? a warning with location but then we should ignore real javadoc tags like author etc ?
            logInfo(
                    "JavaDoc tag [" + annotationName +
                    "] is not treated as an annotation - class could not be resolved"
                    + " at " + enclosingClassName + " in " + enclosingClassFileName + ", line " +
                    tag.getLineNumber()
            );
            return null;
        }

        return JavaDocParser.getRawAnnotation(annotationInterface, tag, enclosingClassName, enclosingClassFileName);
    }

    /**
     * Prints the usage.
     */
    private static void printUsage() {
        System.out.println("backport175 (c) 2002-2005 Jonas Bon�r, Alexandre Vasseur");
        System.out.println(
                "usage: java [options...] org.codehaus.backport175.compiler.AnnotationC [-verbose] -src <path to src dir> | -srcfiles <list of files> | -srcincludes <path to file> -classes <path to classes dir> [-dest <path to destination dir>] [-config <property file>]"
        );
        System.out.println(
                "       -src <path to src dir> - provides the list of source directories separated by 'File.pathSeparator'"
        );
        System.out.println("       -srcfiles <list of files> - provides a comma separated list of source files");
        System.out.println(
                "       -srcincludes <path to file> - provides the path to a file containing the list of source files (one name per line)"
        );
        System.out.println(
                "       -dest <path to destination dir> - optional, if omitted the compiled classes will be written to the initial directory"
        );
        System.out.println(
                "       -config <property file with aliases to the FQN of the annotation interfaces> - optional"
        );
        System.out.println("       -verbose - activates compilation status information");
        System.out.println("");
        System.out.println("Note: only one of -src -srcpath and -srcincludes may be used");
        System.exit(0);
    }

    /**
     * Parses the command line options.
     *
     * @param args the arguments
     * @return a map with the options
     */
    private static Map parseCommandLineOptions(final String[] args) {
        final Map arguments = new HashMap();
        try {
            for (int i = 0; i < args.length; i++) {
                //-verbose has no value
                if (args[i].equals(COMMAND_LINE_OPTION_VERBOSE)) {
                    arguments.put(COMMAND_LINE_OPTION_VERBOSE, "true");
                } else if (args[i].startsWith(COMMAND_LINE_OPTION_DASH)) {
                    String option = args[i];
                    String value = args[++i];
                    arguments.put(option, value);
                }
            }
        } catch (Exception e) {
            System.err.println("options list to compiler is not valid");
            System.exit(1);
        }
        return arguments;
    }

    /**
     * Logs an INFO message (helper)
     *
     * @param message the message
     */
    public void logInfo(final String message) {
        m_handler.info(message);
    }

    /**
     * Splits a string into parts.
     *
     * @param str the string to split
     * @param sep the separator
     * @return the string parts in a string array
     */
    private static String[] split(final String str, final String sep) {
        if (str == null || str.length() == 0) {
            return new String[0];
        }

        int start = 0;
        int idx = str.indexOf(sep, start);
        int len = sep.length();
        List strings = new ArrayList();

        while (idx != -1) {
            strings.add(str.substring(start, idx));
            start = idx + len;
            idx = str.indexOf(sep, start);
        }

        strings.add(str.substring(start));

        return (String[])strings.toArray(new String[strings.size()]);
    }

    /**
     * Load and solve relative to working directory the list of files.
     *
     * @param srcIncludes
     * @return
     */
    private static String[] loadSourceList(final String srcIncludes) {
        File currentDir = new File(".");
        List files = new ArrayList();
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new FileReader(srcIncludes));

            String line = reader.readLine();
            File tmpFile;
            while (line != null) {
                if (line.length() > 0) {
                    tmpFile = new File(currentDir, line);
                    if (!tmpFile.isFile()) {
                        System.err.println("file not found: [" + tmpFile + "]");
                    } else {
                        files.add(tmpFile.getAbsolutePath());
                    }
                }
                line = reader.readLine();
            }
        } catch (IOException ioe) {
            throw new CompilerException("an error occured while reading from pattern file: " + srcIncludes, ioe);
        } finally {
            if (null != reader) {
                try {
                    reader.close();
                } catch (IOException ioe) {
                    //Ignore exception
                }
            }
        }
        return (String[])files.toArray(new String[files.size()]);
    }
}
TOP

Related Classes of org.codehaus.backport175.compiler.AnnotationC

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.