Package com.google.caja.plugin

Source Code of com.google.caja.plugin.Config

// Copyright (C) 2007 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.caja.plugin;

import com.google.caja.config.ConfigUtil;
import com.google.caja.config.WhiteList;
import com.google.caja.lang.css.CssSchema;
import com.google.caja.lang.html.HtmlSchema;
import com.google.caja.lexer.InputSource;
import com.google.caja.lexer.ParseException;
import com.google.caja.reporting.BuildInfo;
import com.google.caja.reporting.MessageQueue;
import com.google.caja.reporting.MessageType;
import com.google.caja.util.Join;
import com.google.caja.util.Lists;
import com.google.caja.util.Pair;
import com.google.caja.util.Sets;
import com.google.caja.util.Strings;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;

/**
* Flag processing for main methods.
*
* @author mikesamuel@gmail.com
*/
public final class Config {
  private final Options options = new Options();

  private final Option INPUT = defineOption(
      "i", "input", "Input URI containing HTML (with optional "
      + "script and style blocks)", true);

  // This output HTML is not standalone, and requires a container to load the
  // supporting JS and set up a virtual document root.
  private final Option OUTPUT_HTML = defineOption(
      "h", "output_html",
      "Output file path for translated HTML (defaults to input with "
      + "\".out.html\")",
      true);

  private final Option OUTPUT_JS = defineOption(
      "j", "output_js",
      "Output file path for translated JS (defaults to input with \".out.js\")",
      true);

  private final Option OUTPUT_BASE = defineOption(
      "o", "out",
      "Path to which the appropriate extension is added to form output files.",
      true);

  private final Option CSS_PROPERTY_WHITELIST = defineOption(
      "cps", "css_prop_schema",
      "A file: or resource: URI of the CSS Property Whitelist to use.",
      true);

  private final Option HTML_ATTRIBUTE_WHITELIST = defineOption(
      "has", "html_attrib_schema",
      "A file: or resource: URI of the HTML attribute Whitelist to use.",
      true);

  private final Option HTML_ELEMENT_WHITELIST = defineOption(
      "hps", "html_property_schema",
      "A file: or resource: URI of the HTML element Whitelist to use.",
      true);

  private final Option BASE_URI = defineOption(
      "base_uri",
      "The URI relative to which URIs in the inputs are resolved.",
      true);

  private final Option FETCHER_BASE = defineOption(
      "fetcher_base",
      "An ancestor of all files which the URI fetcher is allowed to resolve.",
      true);

  private final Option F_URI = defineOption(
      "fu", "f_uri", "A URI which the URI fetcher is allowed to fetch.", true);

  private final Option F_URI_ALL = defineBooleanOption(
      "fua", "f_uri_all", "Allow all URIs to be fetched.");

  private final Option L_URI = defineOption(
      "lu", "l_uri", "A URI which the URI policy is allowed to link to.", true);

  private final Option L_URI_ALL = defineBooleanOption(
      "lua", "l_uri_all", "Allow all URIs to be linked to.");

  private final Option L_URI_RUNTIME = defineBooleanOption(
      "lur", "l_uri_runtime", "Use a runtime URI policy for linked-to URIs.");

  private final Option SERVICE_PORT = defineOption(
      "port",
      "The port on which cajoling service is run.",
      true);

  private final Option ID_CLASS = defineOption(
      "c",
      "id_class",
      "The module ID if it is statically known",
      true);

  private final Option DEBUG_MODE = defineBooleanOption(
      "g", "debug", "Set to add debugging info to cajoled output.");

  private final Option RENDERER = defineOption(
      "r",
      "renderer",
      "The output renderer "
      + Strings.lower(Arrays.toString(SourceRenderMode.values())) + "",
      true);

  private final Option PIPELINE_PRECONDITIONS = defineOption(
      "ppc", "preconds",
      "Space separated properties as described in help text.", true);

  private final Option PIPELINE_GOALS = defineOption(
      "pg", "goals",
      "Space separated properties as described in help text.", true);

  private final Option NO_PRECAJOLED = defineBooleanOption(
      "np", "no_precajoled", "Don't use the precajoled cache");

  private final Option BENCHMARK = defineOption(
      "bm", "benchmark",
      "Run n times and report stats.", true);

  public enum SourceRenderMode {
    MINIFY,
    PRETTY,
    ;
  }

  private final Class<?> mainClass;
  private final PrintWriter stderr;
  private final String usageText;
  private List<URI> inputUris;
  private File outputBase;
  private File outputJsFile;
  private File outputHtmlFile;
  private URI cssPropertyWhitelistUri;
  private URI htmlAttributeWhitelistUri;
  private URI htmlElementWhitelistUri;
  private URI baseUri;
  private File fetcherBase;
  private SourceRenderMode renderer;
  private Set<String> fUris;
  private boolean fUriAll = false;
  private Set<String> lUris;
  private boolean lUriAll = false;
  private boolean lUriRuntime = false;
  private int servicePort;
  private String idClass;
  private Planner.PlanState negGoals = Planner.EMPTY;
  private Planner.PlanState posGoals = Planner.EMPTY;
  private Planner.PlanState negPreconds = Planner.EMPTY;
  private Planner.PlanState posPreconds = Planner.EMPTY;
  private boolean noPrecajoled = false;
  private int benchmark = 0;

  public Config(Class<?> mainClass, PrintStream stderr, String usageText) {
    this(mainClass, new PrintWriter(stderr), usageText);
  }

  public Config(Class<?> mainClass, PrintWriter stderr, String usageText) {
    this.mainClass = mainClass;
    this.stderr = stderr;
    this.usageText = usageText;
  }

  public Collection<URI> getInputUris() { return inputUris; }
  public File getOutputJsFile() { return outputJsFile; }
  public File getOutputHtmlFile() { return outputHtmlFile; }
  public File getOutputBase() { return outputBase; }
  public int getServicePort() { return servicePort; }
  public URI getCssPropertyWhitelistUri() {
    return cssPropertyWhitelistUri;
  }
  public URI getHtmlAttributeWhitelistUri() {
    return htmlAttributeWhitelistUri;
  }
  public URI getHtmlElementWhitelistUri() {
    return htmlElementWhitelistUri;
  }
  public URI getBaseUri() { return baseUri; }
  public File getFetcherBase() { return fetcherBase; }

  public CssSchema getCssSchema(MessageQueue mq) {
    return new CssSchema(
        whitelist(cssPropertyWhitelistUri, mq),
        whitelist(URI.create(
            "resource:///com/google/caja/lang/css/css-extensions-fns.json"),
            mq));
  }

  public HtmlSchema getHtmlSchema(MessageQueue mq) {
    return new HtmlSchema(
        whitelist(htmlElementWhitelistUri, mq),
        whitelist(htmlAttributeWhitelistUri, mq));
  }

  public String getIdClass() { return idClass; }

  public Set<String> getFetchableUris() { return fUris; }

  public boolean hasFetchableUriAll() { return fUriAll; }

  public Set<String> getLinkableUris() { return lUris; }

  public boolean hasLinkableUriAll() { return lUriAll; }

  public boolean hasLinkableUriRuntime() { return lUriRuntime; }

  public SourceRenderMode renderer() { return renderer; }

  public Planner.PlanState goals(Planner.PlanState ps) {
    return ps.without(negGoals).with(posGoals);
  }

  public Planner.PlanState preconditions(Planner.PlanState ps) {
    return ps.without(negPreconds).with(posPreconds);
  }

  public boolean hasNoPrecajoled() { return noPrecajoled; }

  public int getBenchmark() { return benchmark; }

  public boolean processArguments(String[] argv) {
    try {
      CommandLine cl;
      try {
        cl = new BasicParser().parse(options, argv, false);
      } catch (org.apache.commons.cli.ParseException e) {
        usage(e.getMessage(), stderr);
        return false;
      }

      inputUris = Lists.newArrayList();
      for (String input : getOptionValues(cl, INPUT)) {
        URI inputUri;
        try {
          if (input.indexOf(':') >= 0) {
            inputUri = new URI(input);
          } else {
            File inputFile = new File(input);

            if (!inputFile.exists()) {
              usage("File \"" + input + "\" does not exist", stderr);
              return false;
            }
            if (!inputFile.canRead() || inputFile.isDirectory()) {
              usage("File \"" + input + "\" is not a regular file", stderr);
              return false;
            }

            inputUri = inputFile.getAbsoluteFile().toURI();
          }
        } catch (URISyntaxException ex) {
          usage("Input \"" + input + "\" is not a valid URI", stderr);
          return false;
        }

        inputUris.add(inputUri);
      }
      if (inputUris.isEmpty()) {
        usage("Option \"--" + INPUT.getLongOpt() + "\" missing", stderr);
        return false;
      }

      if (cl.getOptionValue(OUTPUT_BASE.getOpt()) != null) {
        outputBase = new File(cl.getOptionValue(OUTPUT_BASE.getOpt()));

        outputJsFile = substituteExtension(outputBase, ".out.js");
        outputHtmlFile = substituteExtension(outputBase, ".out.html");

        if (cl.getOptionValue(OUTPUT_JS.getOpt()) != null) {
          usage("Can't specify both --out and --output_js", stderr);
          return false;
        }
        if (cl.getOptionValue(OUTPUT_HTML.getOpt()) != null) {
          usage("Can't specify both --out and --output_html", stderr);
          return false;
        }
      } else {
        outputJsFile = cl.getOptionValue(OUTPUT_JS.getOpt()) == null
            ? null : new File(cl.getOptionValue(OUTPUT_JS.getOpt()));

        outputHtmlFile = cl.getOptionValue(OUTPUT_HTML.getOpt()) == null
            ? null : new File(cl.getOptionValue(OUTPUT_HTML.getOpt()));

        if (outputJsFile == null && outputHtmlFile == null) {
          usage("Please specify js output via " + OUTPUT_JS.getLongOpt()
                + " &| html output via " + OUTPUT_HTML.getLongOpt(),
                stderr);
          return false;
        }
      }

      try {
        cssPropertyWhitelistUri = new URI(cl.getOptionValue(
            CSS_PROPERTY_WHITELIST.getOpt(),
            "resource:///com/google/caja/lang/css/css-extensions.json"));
        htmlAttributeWhitelistUri = new URI(cl.getOptionValue(
            HTML_ATTRIBUTE_WHITELIST.getOpt(),
            "resource:///com/google/caja/lang/html"
            + "/html4-attributes-extensions.json"));
        htmlElementWhitelistUri = new URI(cl.getOptionValue(
            HTML_ELEMENT_WHITELIST.getOpt(),
            "resource:///com/google/caja/lang/html"
            + "/html4-elements-extensions.json"));

        if (cl.getOptionValue(BASE_URI.getOpt()) != null) {
          baseUri = new URI(cl.getOptionValue(BASE_URI.getOpt()));
        } else {
          baseUri = inputUris.get(0);
        }
      } catch (URISyntaxException ex) {
        stderr.println("Invalid whitelist URI: " + ex.getInput() + "\n    "
                       + ex.getReason());
        return false;
      }

      if (cl.getOptionValue(FETCHER_BASE.getOpt()) != null) {
        fetcherBase = new File(cl.getOptionValue(FETCHER_BASE.getOpt()));
      } else if (Strings.eqIgnoreCase(baseUri.getScheme(), "file")) {
        fetcherBase = new File(baseUri).getParentFile();
      }

      idClass = cl.getOptionValue(ID_CLASS.getOpt(), null);

      String servicePortString;
      try {
        servicePortString = cl.getOptionValue(SERVICE_PORT.getOpt(), "8887");
        servicePort = Integer.parseInt(servicePortString);
      } catch ( NumberFormatException e ) {
        stderr.println(
            "Invalid service port: " + SERVICE_PORT.getOpt() + "\n    "
            + e.getMessage());
        return false;
      }

      fUris = Sets.immutableSet(getOptionValues(cl, F_URI));
      fUriAll = cl.hasOption(F_URI_ALL.getOpt());

      lUris = Sets.immutableSet(getOptionValues(cl, L_URI));
      lUriAll = cl.hasOption(L_URI_ALL.getOpt());
      lUriRuntime = cl.hasOption(L_URI_RUNTIME.getOpt());

      String renderString = cl.getOptionValue(RENDERER.getOpt());
      if (renderString != null) {
        renderer = SourceRenderMode.valueOf(Strings.upper(renderString));
        if (renderer == null) {
          stderr.println("Invalid renderer: " + renderString);
          return false;
        }
      } else {
        renderer = SourceRenderMode.PRETTY;
      }

      boolean debugMode = cl.hasOption(DEBUG_MODE.getOpt());
      boolean onlyJsEmitted = cl.hasOption(OUTPUT_JS.getOpt())
          && !cl.hasOption(OUTPUT_HTML.getOpt());
      if (debugMode) {
        negGoals = negGoals.with(PipelineMaker.ONE_CAJOLED_MODULE);
        posGoals = posGoals.with(PipelineMaker.ONE_CAJOLED_MODULE_DEBUG);
      }
      if (onlyJsEmitted) {
        negGoals = negGoals.with(PipelineMaker.HTML_SAFE_STATIC);
      }

      String preconds = cl.getOptionValue(PIPELINE_PRECONDITIONS.getOpt());
      if (preconds != null) {
        Pair<Planner.PlanState, Planner.PlanState> deltas
            = planDeltas(preconds);
        negPreconds = negPreconds.with(deltas.a);
        posPreconds = posPreconds.with(deltas.b);
      }

      String goals = cl.getOptionValue(PIPELINE_GOALS.getOpt());
      if (goals != null) {
        Pair<Planner.PlanState, Planner.PlanState> deltas = planDeltas(goals);
        negGoals = negGoals.with(deltas.a);
        posGoals = posGoals.with(deltas.b);
      }

      noPrecajoled = cl.hasOption(NO_PRECAJOLED.getOpt());

      benchmark = Integer.valueOf(cl.getOptionValue(BENCHMARK.getOpt(), "0"));

      return true;
    } finally {
      stderr.flush();
    }
  }

  private Pair<Planner.PlanState, Planner.PlanState> planDeltas(String props) {
    props = props.trim();
    List<String> neg = Lists.newArrayList();
    List<String> pos = Lists.newArrayList();
    for (String part : props.split("\\s+")) {
      if (!"".equals(part)) {
        if (part.startsWith("-")) {
          neg.add(part.substring(1));
        } else {
          pos.add(part.substring(part.startsWith("+") ? 1 : 0));
        }
      }
    }
    try {
      return Pair.pair(
          PipelineMaker.planState(Join.join("", neg)),
          PipelineMaker.planState(Join.join("", pos)));
    } catch (IllegalArgumentException ex) {
      stderr.println("Bad prop in " + props + " : " + ex.getMessage());
      return Pair.pair(Planner.EMPTY, Planner.EMPTY);
    }
  }

  public void usage(String msg, PrintWriter out) {
    out.println(BuildInfo.getInstance().getBuildInfo());
    out.println();
    if (msg != null && !"".equals(msg)) {
      out.println(msg);
      out.println();
    }
    new HelpFormatter().printHelp(
        out, HelpFormatter.DEFAULT_WIDTH,
        (mainClass.getSimpleName()
         + " --input <in.html> [--output_js <out.js> | --out <out>]"),
        "\n", options,
        HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD,
        "\n" + usageText, false);
    out.println();
    int maxPlanStateWidth = 0;
    for (Pair<String, String> doc
        : PipelineMaker.getPreconditionDocumentation()) {
      maxPlanStateWidth = Math.max(maxPlanStateWidth, doc.a.length());
    }
    for (Pair<String, String> doc : PipelineMaker.getGoalDocumentation()) {
      maxPlanStateWidth = Math.max(maxPlanStateWidth, doc.a.length());
    }
    String fmtStr = "%" + maxPlanStateWidth + "s | %s";
    out.println(
        "Preconditions (default to " + PipelineMaker.DEFAULT_PRECONDS + ")");
    for (Pair<String, String> doc
            : PipelineMaker.getPreconditionDocumentation()) {
      out.println(String.format(fmtStr, doc.a, doc.b));
    }
    out.println();
    out.println("Goals (default to " + PipelineMaker.DEFAULT_GOALS + ")");
    for (Pair<String, String> doc : PipelineMaker.getGoalDocumentation()) {
      out.println(String.format(fmtStr, doc.a, doc.b));
    }
  }

  private static File substituteExtension(File file, String extension) {
    String fileName = file.getName();
    int lastDot = fileName.lastIndexOf('.');
    if (lastDot < 0) { lastDot = fileName.length(); }
    return new File(file.getParentFile(),
                    fileName.substring(0, lastDot) + extension);
  }

  private static WhiteList whitelist(URI uri, MessageQueue mq) {
    InputSource src = new InputSource(uri);
    try {
      return ConfigUtil.loadWhiteListFromJson(
          uri, ConfigUtil.RESOURCE_RESOLVER, mq);
    } catch (IOException ex) {
      mq.addMessage(MessageType.IO_ERROR, src);
    } catch (ParseException ex) {
      ex.toMessageQueue(mq);
    }
    // Return a Null instance if unable to load.
    return new WhiteList() {
        public Set<String> allowedItems() {
          return Collections.<String>emptySet();
        }
        public Map<String, TypeDefinition> typeDefinitions() {
          return Collections.<String, TypeDefinition>emptyMap();
        }
      };
  }

  private Option defineOption(
      String shortFlag, String longFlag, String help, boolean optional) {
    Option opt = new Option(shortFlag, longFlag, /* hasArg: */ true, help);
    opt.setOptionalArg(optional);
    options.addOption(opt);
    return opt;
  }

  private Option defineBooleanOption(
      String shortFlag, String longFlag, String help) {
    Option opt = new Option(shortFlag, longFlag, false, help);
    opt.setOptionalArg(true);
    options.addOption(opt);
    return opt;
  }

  private Option defineOption(String longFlag, String help, boolean optional) {
    return defineOption(longFlag, longFlag, help, optional);
  }

  private static String[] getOptionValues(CommandLine cl, Option opt) {
    String[] values = cl.getOptionValues(opt.getOpt());
    return values != null ? values : new String[0];
  }

  public static void main(String[] argv) {
    Config config = new Config(Config.class, System.err, "Does some stuff.");
    System.err.println(config.processArguments(argv));
  }
}
TOP

Related Classes of com.google.caja.plugin.Config

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.