package test.utils;

import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternCompiler;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;


import java.io.File;
import java.io.FileInputStream;

/**
 * This TestCase verifies that content of the source files adheres
 * to certain coding practices by matching regular expressions
 * (string patterns):
 *
 * - Verify that Log4J logger is not being used directly
 *   ("org.apache.log4j" is not in source files).
 *
 * - Verify that System.out.println is not used except
 *   in wsdl to/from java tooling.
 *
 * - Verify that log.info(), log.warn(), log.error(), and log.fatal()
 *   use Messages.getMessage() (i18n).
 *
 * - Verify that exceptions are created with Messages.getMessage() (i18n).
 *
 * To add new patterns, search for and append to the
 * private attribute 'avoidPatterns'.
 *
 * Based on code in TestMessages.java.
 */
public class TestSrcContent extends TestCase {
    public TestSrcContent(String name) {
        super(name);
    } // ctor

    public static Test suite() {
        return new TestSuite(TestSrcContent.class);
    }

    private static final String LS = System.getProperty("line.separator");

    private String errors = "";

    /**
     * If this test is run from xml-axis/java, then walk through the source
     * tree (xml-axis/java/src), calling checkFile for each file.
     */
    public void testSourceFiles() {
        String baseDir = System.getProperty("user.dir");
        File   srcDir = new File(baseDir, "src");

        if (srcDir.exists()) {
            walkTree(srcDir);
        }

        if (!errors.equals("")) {
            throw new AssertionFailedError(errors);
        }
    } // testSourceFiles


    /**
     * Walk the source tree
     */
    private void walkTree(File srcDir) {
        File[] files = srcDir.listFiles();
        for (int i = 0; i < files.length; ++i) {
            if (files[i].isDirectory()) {
                walkTree(files[i]);
            }
            else {
                checkFile(files[i]);
            }
        }
    } // walkTree


    static private class FileNameContentPattern
    {
        private PatternCompiler compiler = new Perl5Compiler();
        private PatternMatcher matcher = new Perl5Matcher();

        private Pattern namePattern = null;
        private Pattern contentPattern = null;
        private boolean expectContent = true;

        FileNameContentPattern(String namePattern,
                               String contentPattern,
                               boolean expectContentInFile)
        {
            try {
                this.namePattern = compiler.compile(namePattern);
                this.contentPattern = compiler.compile(contentPattern);
                this.expectContent = expectContentInFile;
            }
            catch (MalformedPatternException e) {
                throw new AssertionFailedError(e.getMessage());
            }
        }

        /**
         * This is not a match IFF
         *  - the name matches, AND
         *  - the content is not as expected
         */
        boolean noMatch(String name, String content)
        {
            return
                matcher.matches(name, namePattern) &&
                matcher.contains(content, contentPattern) != expectContent;
        }

        String getContentPattern() { return contentPattern.getPattern(); }

        boolean getExpectContent() { return expectContent; }
    };

    /**
     * Patterns to be checked. Each pattern has three parameters:
     *   (i) a pattern that matches filenames that are to be checked,
     *  (ii) a pattern to be searched for in the chosen files
     * (iii) whether the pattern is to be allowed (typically false indicating
     *       not allowed)
     * See the Axis Developer's Guide for more information.
     */
    private static final FileNameContentPattern avoidPatterns[] =
        {
            //**
            //** For escape ('\'), remember that Java gets first dibs..
            //** so double-escape for pattern-matcher to see it.
            //**

            // Verify that java files do not use Log4j
            //
            //new FileNameContentPattern(".+\\.java",
            //                           "org\\.apache\\.log4j", false),
            new FileNameContentPattern(".+([\\\\/])"
                                       + "java\\1"
//                                       + "(?!src\\1org\\1apache\\1axis\\1client\\1HappyClient\\.java)"
                                       + "([a-zA-Z0-9_]+\\1)*"
                                       + "[^\\\\/]+\\.java",
                                       "org\\.apache\\.log4j", false),

            // Verify that axis java files do not use System.out.println
            // or System.err.println, except:
            //   - utils/tcpmon.java
            //   - providers/BSFProvider.java
            //   - utils/CLArgsParser.java
            //   - Version.java
            //   - tooling in 'org/apache/axis/wsdl'            
            //   - client/AdminClient.java
            //   - utils/Admin.java
            new FileNameContentPattern(".+([\\\\/])"
                                       + "java\\1src\\1org\\1apache\\1axis\\1"
                                       + "(?!utils\\1tcpmon\\.java"
                                       + "|client\\1AdminClient\\.java"
                                       + "|utils\\1Admin\\.java"
                                       + "|utils\\1SOAPMonitor\\.java"
                                       + "|providers\\1BSFProvider\\.java"
                                       + "|utils\\1CLArgsParser\\.java"
                                       + "|transport\\1jms\\1SimpleJMSListener\\.java"
                                       + "|Version\\.java"
                                       + "|wsdl\\1)"
                                       + "([a-zA-Z0-9_]+\\1)*"
                                       + "[^\\\\/]+\\.java",
                                       "System\\.(out|err)\\.println", false),

            // Verify that internationalization is being used properly
            // with logger.  Exceptions:
            //   - all log.debug calls
            //   - client/AdminClient.java
            //   - utils/tcpmon.java
            //   - utils/Admin.java
            //   - handlers/LogMessage.java
            //   - tooling in 'org/apache/axis/wsdl'
            //
            new FileNameContentPattern(".+([\\\\/])"
                                       + "java\\1src\\1org\\1apache\\1axis\\1"
                                       + "(?!utils\\1tcpmon\\.java"
                                       + "|client\\1AdminClient\\.java"
                                       + "|utils\\1Admin\\.java"
                                       + "|utils\\1SOAPMonitor\\.java"
                                       + "|handlers\\1LogMessage\\.java"
                                       + "|wsdl\\1)"
                                       + "([a-zA-Z0-9_]+\\1)*"
                                       + "[^\\\\/]+\\.java",
                                       
                                       "log\\.(info|warn|error|fatal)"
                                       + "[ \\t]*\\("
                                       + "(?=[ \\t]*\\\")",
                                       
                                       false),

            // Verify that exceptions are built with messages.

            new FileNameContentPattern(".+([\\\\/])"
                                       + "java\\1src\\1org\\1apache\\1axis\\1"
                                       + "([a-zA-Z0-9_]+\\1)*"
                                       + "[^\\\\/]+\\.java",
                                       "new[ \\t]+[a-zA-Z0-9_]*"
                                       + "Exception\\(\\)",
                                       false),

            // Verify that we don't explicitly create NPEs.

            new FileNameContentPattern(".+([\\\\/])"
                                       + "java\\1src\\1org\\1apache\\1axis\\1"
                                       + "([a-zA-Z0-9_]+\\1)*"
                                       + "[^\\\\/]+\\.java",
                                       "new[ \\t]+"
                                       + "NullPointerException",
                                       false),

        };

    private void checkFile(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            byte[] bytes = new byte[fis.available()];
            fis.read(bytes);
            String content = new String(bytes);

            for (int i = 0; i < avoidPatterns.length; i++) {
                if (avoidPatterns[i].noMatch(file.getPath(), content)) {
                //                if (content.indexOf(avoidStrings[i]) >= 0) {
                    errors = errors
                        + "File: " + file.getPath() + ": "
                        + (avoidPatterns[i].getExpectContent()
                           ? "Expected: "
                           : "Unexpected: ")
                        + avoidPatterns[i].getContentPattern()
                        + LS;
                }
            }
        }
        catch (Throwable t) {
            errors = errors
                + "File: " + file.getPath()
                + ": " + t.getMessage()
                + LS;
        }
    } // checkFile
}
