com.google.gxp.compiler.cli.GxpcFlags.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gxp.compiler.cli.GxpcFlags.java

Source

/*
 * Copyright (C) 2008 Google Inc.
 *
 * 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.google.gxp.compiler.cli;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.io.CharStreams;
import com.google.gxp.compiler.Configuration;
import com.google.gxp.compiler.Phase;
import com.google.gxp.compiler.alerts.Alert.Severity;
import com.google.gxp.compiler.alerts.AlertPolicy;
import com.google.gxp.compiler.alerts.ConfigurableAlertPolicy;
import com.google.gxp.compiler.base.OutputLanguage;
import com.google.gxp.compiler.codegen.CodeGeneratorFactory;
import com.google.gxp.compiler.codegen.DefaultCodeGeneratorFactory;
import com.google.gxp.compiler.fs.FileRef;
import com.google.gxp.compiler.fs.FileSystem;
import com.google.gxp.compiler.fs.SourcePathFileSystem;
import com.google.gxp.compiler.i18ncheck.UnextractableContentAlert;
import com.google.gxp.compiler.parser.FileSystemEntityResolver;
import com.google.gxp.compiler.parser.SourceEntityResolver;

import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.util.*;

import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;

/**
 * The GXP compiler, "gxpc". The command line interface for generating code and
 * XMB files from GXP files.
 */
class GxpcFlags implements Configuration {
    private final CommandLine commandLine;
    private final ImmutableSet<FileRef> sourceFiles;
    private final ImmutableSet<FileRef> schemaFiles;
    private final ImmutableSet<OutputLanguage> outputLanguages;
    private final DefaultCodeGeneratorFactory codeGeneratorFactory;
    private final ImmutableSet<FileRef> allowedOutputFiles;
    private final FileRef dependencyFile;
    private final FileRef propertiesFile;
    private final boolean isVerboseEnabled;
    private final boolean isDebugEnabled;
    private final AlertPolicy alertPolicy;
    private final ImmutableSortedSet<Phase> dotPhases;
    private final SourceEntityResolver sourceEntityResolver;

    /**
     * Creates an instance of the compiler based on command-line arguments.
     *
     * @param fs underlying {@code FileSystem} that filenames in {@code args}
     * refer to
     * @param defaultDir default directory for source and output dirs
     * @param args command-line arguments. See <code>HELP_*</code> variables
     * defined in {@link GxpcFlags} for accepted flags
     * @throws Flags.UsageError if there is an error parsing the command line
     * arguments
     */
    GxpcFlags(FileSystem fs, FileRef defaultDir, String... args) throws CmdLineException, IOException {

        // If there is only one argument, and it starts with an '@', then treat it
        // as an options file, relative to the current working directory.
        if ((args.length == 1) && (args[0].startsWith("@"))) {
            FileRef optionsFile = defaultDir.join(args[0].substring(1));
            Reader in = optionsFile.openReader(Charsets.UTF_8);
            List<String> lines = CharStreams.readLines(in);
            in.close();
            List<String> parsedTokens = Lists.newArrayList();
            for (String line : lines) {
                for (String token : line.trim().split("\\s+")) {
                    if (token.length() > 0) {
                        parsedTokens.add(token);
                    }
                }
            }
            args = parsedTokens.toArray(new String[parsedTokens.size()]);
        }

        commandLine = new CommandLine(args);

        Set<FileRef> underlyingInputFiles = getFileRefs(fs, commandLine.trailingArgs);

        FileRef outputDir = (commandLine.FLAG_dir == null) ? defaultDir : fs.parseFilename(commandLine.FLAG_dir);

        List<FileRef> sourcePaths = (commandLine.FLAG_source == null) ? Collections.singletonList(defaultDir)
                : fs.parseFilenameList(commandLine.FLAG_source);

        SourcePathFileSystem sourcePathFs = new SourcePathFileSystem(fs, sourcePaths, underlyingInputFiles,
                outputDir);

        sourceFiles = ImmutableSet.copyOf(sourcePathFs.getSourceFileRefs());
        schemaFiles = getFileRefs(fs, commandLine.FLAG_schema);

        // Compute Output Languages
        Set<OutputLanguage> tmpOutputLanguages = EnumSet.noneOf(OutputLanguage.class);
        for (String outputLanguage : commandLine.FLAG_output_language) {
            tmpOutputLanguages.add(OutputLanguage.valueOf(outputLanguage.toUpperCase()));
        }
        outputLanguages = ImmutableSet.copyOf(tmpOutputLanguages);

        allowedOutputFiles = getFileRefs(sourcePathFs, commandLine.FLAG_output);

        alertPolicy = computeAlertPolicy(commandLine.FLAG_warn, commandLine.FLAG_error);

        // Compute Dependency File
        dependencyFile = (commandLine.FLAG_depend == null) ? null : fs.parseFilename(commandLine.FLAG_depend);

        // Compute Properties File
        propertiesFile = (commandLine.FLAG_output_properties && commandLine.FLAG_message_source != null)
                ? outputDir.join("/" + commandLine.FLAG_message_source.replace(".", "/") + "_en.properties")
                : null;

        isVerboseEnabled = commandLine.FLAG_verbose;
        isDebugEnabled = commandLine.FLAG_g;

        // Compute Dot Phases
        dotPhases = computeDotPhases(commandLine.getParser(), commandLine.FLAG_dot);

        // Compute SourceEntityResolver
        // use the sourcePathFs so that gxp:///foo/bar is resolved in a way that
        // includes both source files and genfiles
        sourceEntityResolver = new FileSystemEntityResolver(sourcePathFs);

        // Compute CodeGeneratorFactory (Always do this last)
        codeGeneratorFactory = new DefaultCodeGeneratorFactory();
        codeGeneratorFactory.setRuntimeMessageSource(commandLine.FLAG_message_source);
        codeGeneratorFactory.setDynamicModeEnabled(commandLine.FLAG_dynamic);
        codeGeneratorFactory.setSourceFiles(getSourceFiles());
        codeGeneratorFactory.setSchemaFiles(getSchemaFiles());
        codeGeneratorFactory.setSourcePaths(sourcePaths);
        codeGeneratorFactory.setAlertPolicy(getAlertPolicy());
    }

    public boolean showHelp() {
        return commandLine.FLAG_help;
    }

    public void printHelp(Appendable out) throws IOException {
        out.append(commandLine.getUsage());
    }

    ////////////////////////////////////////////////////////////////////////////////
    // Supplemental Computation Functions
    ////////////////////////////////////////////////////////////////////////////////

    /**
     * Iterate over an {@code Iterable} of flags treating each as a filename in the
     * supplied {@link FileSystem}.
     *
     * @return an {@link ImmutableSet} of the results.
     */
    private static ImmutableSet<FileRef> getFileRefs(FileSystem fs, Iterable<String> filenames) {
        Set<FileRef> result = Sets.newHashSet();
        for (String filename : filenames) {
            result.add(fs.parseFilename(filename));
        }
        return ImmutableSet.copyOf(result);
    }

    // TODO(laurence): add more general support for AlertPolicy configuration
    private static final AlertPolicy computeAlertPolicy(List<String> warnFlags, List<String> errorFlags) {
        ConfigurableAlertPolicy result = new ConfigurableAlertPolicy();
        configureAlertPolicy(result, warnFlags, Severity.WARNING);
        configureAlertPolicy(result, errorFlags, Severity.ERROR);
        if (warnFlags.contains("error")) {
            result.setTreatWarningsAsErrors(true);
        }

        return result;
    }

    private static final void configureAlertPolicy(ConfigurableAlertPolicy alertPolicy, List<String> flags,
            Severity severity) {
        if (flags.contains("i18n")) {
            alertPolicy.setSeverity(UnextractableContentAlert.class, severity);
        }
    }

    private static ImmutableSortedSet<Phase> computeDotPhases(CmdLineParser parser, List<String> phaseNames)
            throws CmdLineException {
        SortedSet<Phase> result = Sets.newTreeSet();
        if (phaseNames.contains("*")) {
            result.addAll(Arrays.asList(Phase.values()));
        } else {
            for (String phaseName : phaseNames) {
                phaseName = phaseName.trim();
                if (phaseName.length() > 0) {
                    phaseName = phaseName.toUpperCase().replace("-", "_");
                    Phase phase;
                    try {
                        phase = Phase.valueOf(phaseName);
                    } catch (IllegalArgumentException iax) {
                        throw new CmdLineException(parser, "illegal phase name in --dot flag: " + phaseName);
                    }
                    result.add(phase);
                }
            }
        }
        return ImmutableSortedSet.copyOfSorted(result);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // Configuration Implementation
    ////////////////////////////////////////////////////////////////////////////////

    public Set<FileRef> getSourceFiles() {
        return sourceFiles;
    }

    public Set<FileRef> getSchemaFiles() {
        return schemaFiles;
    }

    public Set<OutputLanguage> getOutputLanguages() {
        return outputLanguages;
    }

    public long getCompilationVersion() {
        return 0;
    }

    public CodeGeneratorFactory getCodeGeneratorFactory() {
        return codeGeneratorFactory;
    }

    public Set<FileRef> getAllowedOutputFiles() {
        return allowedOutputFiles;
    }

    public FileRef getDependencyFile() {
        return dependencyFile;
    }

    public FileRef getPropertiesFile() {
        return propertiesFile;
    }

    public boolean isVerboseEnabled() {
        return isVerboseEnabled;
    }

    public boolean isDebugEnabled() {
        return isDebugEnabled;
    }

    public AlertPolicy getAlertPolicy() {
        return alertPolicy;
    }

    public SortedSet<Phase> getDotPhases() {
        return dotPhases;
    }

    public SourceEntityResolver getEntityResolver() {
        return sourceEntityResolver;
    }

    ////////////////////////////////////////////////////////////////////////////////
    // Command Line
    ////////////////////////////////////////////////////////////////////////////////

    /**
     * Bucket for holding the output of flags parsing. It doesn't do any fancy
     * processing of the flags.
     */
    private static class CommandLine {
        private final CmdLineParser parser;

        private CommandLine(String[] args) throws CmdLineException {
            parser = new CmdLineParser(this);
            parser.parseArgument(args);
        }

        public CmdLineParser getParser() {
            return parser;
        }

        public String getUsage() {
            StringWriter sw = new StringWriter();
            sw.append("Usage: gxpc [flags...] [args...]\n");
            parser.printUsage(sw, null);
            return sw.toString();
        }

        @Argument
        public List<String> trailingArgs = Lists.newArrayList();

        @Option(name = "--help", usage = "display this help message")
        public boolean FLAG_help = false;

        @Option(name = "--dir", usage = "output directory")
        public String FLAG_dir = null;

        @Option(name = "--schema", usage = "a schema file used for compilation; can be repeated.")
        public List<String> FLAG_schema = Lists.newArrayList();

        @Option(name = "--output_language", usage = "output files for this language; can be repeated.")
        public List<String> FLAG_output_language = Lists.newArrayList();

        @Option(name = "--output", usage = "output this file; can be repeated. If not specified,\n"
                + "all files will be output.")
        public List<String> FLAG_output = Lists.newArrayList();

        @Option(name = "--warn", usage = "Sets warning options. VAL can be one of:\n"
                + "i18n (enable i18n warnings),\n" + "error (warnings are errors)")
        public List<String> FLAG_warn = Lists.newArrayList();

        @Option(name = "--error", usage = "Sets error options. VAL can be one of:\n" + "i18n (enable i18n errors)")
        public List<String> FLAG_error = Lists.newArrayList();

        @Option(name = "--source", usage = "base directory for source")
        public String FLAG_source = null;

        @Option(name = "--dynamic", usage = "indicate dynamic mode")
        public boolean FLAG_dynamic = false;

        @Option(name = "--output_properties", usage = "indicates that gxpc should output a properties file")
        public boolean FLAG_output_properties = false;

        @Option(name = "--message_source", usage = "Message source for retrieving messages at runtime.\n"
                + "eg: com.google.foo.bar_messages")
        public String FLAG_message_source = null;

        @Option(name = "--depend", usage = "location of dependency info file; enables dependency\n" + "checking")
        public String FLAG_depend = null;

        @Option(name = "--verbose", usage = "enable verbose mode")
        public boolean FLAG_verbose = false;

        @Option(name = "--g", usage = "include debugging comments in HTML output")
        public boolean FLAG_g = false;

        @Option(name = "--dot", usage = "phase to produce graphviz \"dot\" output for;\n"
                + "can be repeated (useful for debugging compiler)")
        public List<String> FLAG_dot = Lists.newArrayList();
    }
}