Package com.google.caja.precajole

Source Code of com.google.caja.precajole.PrecajoleTask

// Copyright (C) 2011 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.precajole;

import com.google.caja.lexer.CharProducer;
import com.google.caja.lexer.FilePosition;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.parser.ParserContext;
import com.google.caja.parser.js.CajoledModule;
import com.google.caja.parser.js.StringLiteral;
import com.google.caja.parser.js.UncajoledModule;
import com.google.caja.parser.quasiliteral.ModuleManager;
import com.google.caja.parser.quasiliteral.opt.ArrayIndexOptimization;
import com.google.caja.plugin.ExpressionSanitizerCaja;
import com.google.caja.plugin.PluginMeta;
import com.google.caja.plugin.UriFetcher;
import com.google.caja.plugin.templates.QuasiUtil;
import com.google.caja.reporting.BuildInfo;
import com.google.caja.reporting.EchoingMessageQueue;
import com.google.caja.reporting.MessageContext;
import com.google.caja.reporting.MessageLevel;
import com.google.caja.reporting.MessageQueue;
import com.google.caja.util.ContentType;
import com.google.common.base.Charsets;
import com.google.common.io.Files;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class PrecajoleTask extends Task {

  private String spec;
  private String dest;
  private final List<String> deps = new ArrayList<String>();
  private StaticPrecajoleMap map;

  public void setSpec(String value) { spec = value; }

  public void setDest(String value) { dest = value; }

  public void setDepend(String value) {
    deps.add(value);
  }

  @Override
  public void execute() {
    if (spec == null) {
      throw new BuildException("missing spec= attribute");
    }
    if (dest == null) {
      throw new BuildException("missing dest= attribute");
    }

    long now = System.currentTimeMillis();

    map = new StaticPrecajoleMap(dest);
    outdatedCheck();

    try {
      new SpecHandler(spec).process();
      map.setModTime(now);
      map.finish();
    } catch (Exception e) {
      throw new BuildException(e);
    }
  }

  // If any dependency files are newer than map, force rebuild all
  private void outdatedCheck() {
    long mt = map.getModTime();
    if (mt <= new File(spec).lastModified()) {
      map.setModTime(0);
      return;
    }
    for (String dep : deps) {
      if (map.getModTime() <= new File(dep).lastModified()) {
        map.setModTime(0);
        return;
      }
    }
  }

  private class SpecHandler extends DefaultHandler {
    private final File specFile;
    private final File specDir;

    private Locator here = null;
    private String precajoleFile = null;
    private String precajoleDir = null;
    private String cdata = "";
    private List<String> uris = null;

    public SpecHandler(String filename) {
      specFile = new File(filename);
      specDir = specFile.getParentFile();
    }

    public void process()
        throws SAXException, FileNotFoundException, IOException {
      XMLReader sax = XMLReaderFactory.createXMLReader();
      sax.setContentHandler(this);
      sax.parse(new InputSource(new FileInputStream(specFile)));
    }

    @Override
    public void setDocumentLocator(Locator here) {
      this.here = here;
    }

    @Override
    public void startElement(
        String uri, String name, String qName, Attributes attrs)
    throws SAXException
    {
      if ("precajole-spec".equals(name)) {
        // ignore
      } else if ("precajole".equals(name)) {
        startPrecajole(attrs);
      } else if ("uri".equals(name)) {
        startUri();
      } else {
        throw new SAXParseException("Unknown tag " + name, here);
      }
    }

    @Override
    public void endElement(String uri, String name, String qName)
    throws SAXException {
      if ("precajole-spec".equals(name)) {
        // ignore
      } else if ("precajole".equals(name)) {
        endPrecajole();
      } else if ("uri".equals(name)) {
        endUri();
      } else {
        throw new SAXParseException("Unknown tag " + name, here);
      }
    }

    @Override
    public void characters(char[] ch, int start, int len) {
      cdata += new String(ch, start, len);
    }

    private void startPrecajole(Attributes attrs) throws SAXParseException {
      if (attrs != null) {
        precajoleDir = attrs.getValue("dir");
        precajoleFile = attrs.getValue("file");
        if (precajoleDir != null && precajoleFile != null) {
          throw new SAXParseException("can't use both dir= and file=", here);
        }
        if (precajoleDir != null || precajoleFile != null) {
          uris = new ArrayList<String>();
          return;
        }
      }
      throw new SAXParseException("missing file= or dir= attribute", here);
    }

    private void startUri() {
      cdata = "";
    }

    private void endUri() throws SAXParseException {
      if (cdata != null) {
        cdata = cdata.trim();
        if (!cdata.isEmpty()) {
          String uri = StaticPrecajoleMap.normalizeUri(cdata);
          uris.add(uri);
          if (uri.startsWith("http:")) {
            uris.add("https:" + uri.substring("http:".length()));
          }
          return;
        }
      }
      throw new SAXParseException("<uri> has no content", here);
    }

    private void endPrecajole() throws SAXException {
      if (precajoleDir != null) {
        cajoleDir(precajoleDir);
      } else {
        cajoleFile(uris, precajoleFile);
      }
    }

    private void cajoleFile(List<String> uris, String name) throws SAXException {
      File f = new File(specDir, name);
      if (f.exists() && f.lastModified() < map.getModTime()) {
        return;
      }
      String text = readFile(f);
      CajoledModule cajoled = cajole(text, name);
      map.put(uris, text, cajoled);
    }

    private void cajoleDir(String dirName) throws SAXException {
      DirectoryScanner ds = new DirectoryScanner();
      ds.setBasedir(specDir);
      String includes[] = { dirName + "/**/*.js" };
      ds.setIncludes(includes);
      ds.scan();
      for (String fileName : ds.getIncludedFiles()) {
        List<String> suburis = new ArrayList<String>();
        for (String uri : uris) {
          String suburi = uri + "/" + fileName;
          suburi = StaticPrecajoleMap.normalizeUri(suburi);
          suburis.add(suburi);
        }
        cajoleFile(suburis, fileName);
      }
    }

    private String readFile(File f) throws SAXException {
      try {
        return Files.toString(f, Charsets.UTF_8);
      } catch (IOException e) {
        throw new SAXException(e);
      }
    }

    private CajoledModule cajole(String text, String name) {
      MessageQueue mq = new EchoingMessageQueue(
          new PrintWriter(System.err), new MessageContext(), false);
      PluginMeta pm = new PluginMeta();
      pm.setPrecajoleMap(null);
      ModuleManager mgr = new ModuleManager(
          pm, BuildInfo.getInstance(),
          UriFetcher.NULL_NETWORK, mq);
      UncajoledModule input = uncajoled(text, name, mq);

      // TODO(felix8a): maybe should use compilation pipeline
      ArrayIndexOptimization.optimize(input);
      ParseTreeNode result = new ExpressionSanitizerCaja(mgr, null)
          .sanitize(input);

      if (mq.hasMessageAtLevel(MessageLevel.ERROR)) {
        throw new BuildException("Failed to cajole " + name);
      }
      if (!(result instanceof CajoledModule)) {
        throw new BuildException("No CajoledModule for " + name);
      }
      return (CajoledModule) result;
    }

    private UncajoledModule uncajoled(
        String text, String name, MessageQueue mq)
    {
      String imaginaryUri = "precajole:///" + name;
      try {
        ParseTreeNode node = new ParserContext(mq)
            .withInput(ContentType.JS)
            .withInput(CharProducer.Factory.create(
                new StringReader(text),
                new com.google.caja.lexer.InputSource(new URI(imaginaryUri))))
            .build();
        // TODO(felix8a): duplicated from SafeHtmlMaker
        node = QuasiUtil.quasiStmt(
            ""
            + "try {"
            + "  @scriptBody;"
            + "} catch (ex___) {"
            + "  ___./*@synthetic*/ getNewModuleHandler()"
            + "      ./*@synthetic*/ handleUncaughtException("
            + "          ex___, onerror, @sourceFile, @line);"
            + "}",
            "scriptBody", node,
            "sourceFile", StringLiteral.valueOf(
                FilePosition.UNKNOWN, imaginaryUri),
            "line", StringLiteral.valueOf(
                FilePosition.UNKNOWN, "0"));
        return UncajoledModule.of(node);
      } catch (Exception e) {
        throw new BuildException(e);
      }
    }
  }
}
TOP

Related Classes of com.google.caja.precajole.PrecajoleTask

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.