com.grossbart.forbiddenfunction.FFRunner.java Source code

Java tutorial

Introduction

Here is the source code for com.grossbart.forbiddenfunction.FFRunner.java

Source

/******************************************************************************* 
 * 
 * Copyright 2015 Zack Grossbart
 *
 * Licensed 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 com.grossbart.forbiddenfunction;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;

import com.google.common.collect.Lists;
import com.google.javascript.jscomp.ErrorManager;

import org.apache.commons.io.FileUtils;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.spi.BooleanOptionHandler;

/**
 * The runner handles all argument processing and sets up the precompile process.  
 */
public class FFRunner {

    @Option(name = "--help", handler = BooleanOptionHandler.class, usage = "Displays this message")
    private boolean m_displayHelp = false;

    /**
     * This enumeration handles all of the log level argument for the command line of 
     * this application.
     */
    private enum LogLevel {
        ALL {
            @Override
            public Level getLevel() {
                return Level.ALL;
            }
        },

        CONFIG {
            @Override
            public Level getLevel() {
                return Level.CONFIG;
            }
        },

        FINE {
            @Override
            public Level getLevel() {
                return Level.FINE;
            }
        },

        FINER {
            @Override
            public Level getLevel() {
                return Level.FINER;
            }
        },

        FINEST {
            @Override
            public Level getLevel() {
                return Level.FINEST;
            }
        },

        INFO {
            @Override
            public Level getLevel() {
                return Level.INFO;
            }
        },

        OFF {
            @Override
            public Level getLevel() {
                return Level.OFF;
            }
        },

        SEVERE {
            @Override
            public Level getLevel() {
                return Level.SEVERE;
            }
        },

        WARNING {
            @Override
            public Level getLevel() {
                return Level.WARNING;
            }
        };

        public abstract Level getLevel();
    }

    @Option(name = "--logging_level", usage = "The logging level (standard java.util.logging.Level values) for Compiler progress. "
            + "Does not control errors or warnings for the JavaScript code under compilation")
    private LogLevel m_loggingLevel = LogLevel.WARNING;

    @Argument
    private List<String> m_js = Lists.newArrayList();

    @Option(name = "-funcs", required = true, usage = "The file containing list of forbidden function names. You may specify multiple")
    private List<String> m_forbidden = Lists.newArrayList();

    @Option(name = "--charset", usage = "Input and output charset for all files. By default, we "
            + "accept UTF-8 as input and output US_ASCII")
    private String m_charset = "UTF-8";

    @Option(name = "--print_tree", handler = BooleanOptionHandler.class, usage = "Prints out the parse tree and exits")
    private boolean m_printTree = false;

    @Option(name = "--no_validate", handler = BooleanOptionHandler.class, usage = "Pass this argument to skip the pre-parse file validation step.  This is faster, but won't "
            + "provide good error messages if the input files are invalid JavaScript.")
    private boolean m_preparse = true;

    @Option(name = "--flagfile", usage = "A file containing additional command-line options.")
    private String m_flagFile = "";

    private StringBuffer m_mainFiles = new StringBuffer();

    /**
     * Process the flags file and add the argument values to the current class.
     * 
     * @param out    the output stream for printing errors while parsing the arguments
     * 
     * @exception CmdLineException
     *                   if there's an error parsing the arguments
     * @exception IOException
     *                   if there is an exception reading the file
     */
    private void processFlagFile(PrintStream out) throws CmdLineException, IOException {
        if (m_flagFile == null || m_flagFile.trim().length() == 0) {
            return;
        }

        List<String> argsInFile = Lists.newArrayList();
        File flagFileInput = new File(m_flagFile);

        String flags = FileUtils.readFileToString(flagFileInput, m_charset);

        StringTokenizer tokenizer = new StringTokenizer(flags);

        while (tokenizer.hasMoreTokens()) {
            argsInFile.add(tokenizer.nextToken());
        }

        m_flagFile = "";

        CmdLineParser parserFileArgs = new CmdLineParser(this);
        parserFileArgs.parseArgument(argsInFile.toArray(new String[] {}));

        // Currently we are not supporting this (prevent direct/indirect loops)
        if (!m_flagFile.equals("")) {
            out.println("ERROR - Arguments in the file cannot contain --flagfile option.");
        }
    }

    private void readForbiddenFuncts(ForbiddenFunction ff) throws IOException {
        for (String f : m_forbidden) {
            File file = new File(f);
            List<String> externs = FileUtils.readLines(file, m_charset);

            for (String extern : externs) {
                ff.addForbiddenFuncion(extern);
            }
        }
    }

    /**
     * Add files for compilation.
     * 
     * @param slim   the compiler instance
     * @param files  the files to add
     * 
     * @return true if the files were properly validated or false otherwise
     * @exception IOException
     *                   if there is an error reading the files
     */
    private boolean addFiles(ForbiddenFunction slim, List<String> files) throws IOException {
        ArrayList<File> readFiles = new ArrayList<File>();

        for (String file : files) {
            File f = new File(file);
            if (readFiles.contains(f)) {
                continue;
            }

            String contents = FileUtils.readFileToString(f, m_charset);

            if (m_preparse) {
                ErrorManager mgr = slim.validate(f.getAbsolutePath(), contents);
                if (mgr.getErrorCount() != 0) {
                    mgr.generateReport();
                    return false;
                }
            }

            m_mainFiles.append(contents + "\n");

            ForbiddenFunction.getLogger().log(Level.INFO, "Adding main file: " + f.getAbsoluteFile());

            slim.addSourceFile(new JSFile(f.getAbsolutePath(), contents));
        }

        return true;
    }

    /**
     * Print the usage of this class.
     * 
     * @param parser the parser of the command line arguments
     */
    private static void printUsage(CmdLineParser parser) {
        System.out.println("java FFRunner [options...] arguments...\n");
        // print the list of available options
        parser.printUsage(System.out);
        System.out.println();
    }

    private List<String> check() throws IOException {
        ForbiddenFunction ff = new ForbiddenFunction();

        readForbiddenFuncts(ff);

        ff.setLoggingLevel(m_loggingLevel.getLevel());

        ForbiddenFunction.getLogger().log(Level.INFO, "Compiling with character set " + m_charset);
        ff.setCharset(m_charset);

        if (!addFiles(ff, m_js)) {
            return new ArrayList<String>();
        }

        return ff.check();
    }

    /**
     * The main API entry point.
     * 
     * @param args   the arguments for this process
     * 
     * @return a list containing any errors
     */

    public static List<String> run(String[] args) {

        FFRunner runner = new FFRunner();

        // parse the command line arguments and options
        CmdLineParser parser = new CmdLineParser(runner);
        parser.setUsageWidth(80); // width of the error display area

        if (args.length == 0) {
            printUsage(parser);
            return new ArrayList<String>();
        }

        try {
            parser.parseArgument(args);
        } catch (CmdLineException e) {
            System.out.println(e.getMessage() + '\n');
            printUsage(parser);
            return new ArrayList<String>();
        }

        try {
            runner.processFlagFile(System.out);

            if (runner.m_displayHelp) {
                parser.printUsage(System.out);
                return new ArrayList<String>();
            }

            return runner.check();
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<String>();
        }
    }

    /**
     * The main entry point.
     * 
     * @param args   the arguments for this process
     */
    public static void main(String[] args) {
        List<String> errors = run(args);
        if (errors.size() > 0) {
            System.out.println("There were forbidden functions.");
            for (String err : errors) {
                System.out.println(err);
            }
        }
    }
}