Package com.google.caja.precajole

Source Code of com.google.caja.precajole.StaticPrecajoleMap$Index

// 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.SomethingWidgyHappenedError;
import com.google.caja.parser.js.CajoledModule;
import com.google.caja.util.Lists;
import com.google.caja.util.Maps;
import com.google.caja.util.Strings;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.binary.Hex;

/**
* This is a PrecajoleMap that looks up precajoled modules from a
* directory in the caja jar (or in the filesystem).
* <p>
* The directory contains serialized baked CajoledModules, stored
* one per file.  Filenames are hex-encoded sha1 key of the uncajoled
* source text.
* <p>
* There's also a file index.dat that contains a serialized map
* of URI -> key.
*/

public class StaticPrecajoleMap implements PrecajoleMap {

  private static String SUBDIR_PATH = "com/google/caja/precajole/data/";
  private static String INDEX_NAME = "index.dat";

  public static StaticPrecajoleMap getInstance() {
    return InstanceHolder.instance;
  }

  private static class InstanceHolder {
    static StaticPrecajoleMap instance = new StaticPrecajoleMap("");
  }

  private static class Entry implements Serializable {
    private static final long serialVersionUID = 1L;
    final String[] uris;
    final String source;
    final CajoledModule minified;
    final CajoledModule pretty;
    final String id;

    Entry(String[] uris, String source, CajoledModule cajoled) {
      this.uris = uris;
      this.source = normalizeSource(source);
      this.minified = cajoled.flatten(true);
      this.pretty = cajoled.flatten(false);
      this.id = idForSource(this.source);
    }

    static Entry from(byte[] serial) {
      return (Entry) deserialize(serial);
    }
  }

  private static class Index implements Serializable {
    private static final long serialVersionUID = 1L;
    final public Map<String, String> map = Maps.newHashMap();
    public long modTime = 0L// millisecond timestamp
  }

  //----

  private final String dir;
  private final Index index;

  public StaticPrecajoleMap(File baseDir) {
    this(baseDir.toString());
  }

  public StaticPrecajoleMap(String baseDir) {
    if (!baseDir.equals("") && !baseDir.endsWith("/")) {
      baseDir += "/";
    }
    this.dir = baseDir + SUBDIR_PATH;
    this.index = readIndex();
  }

  private Index readIndex() {
    Object o = deserialize(load(INDEX_NAME));
    if (o != null && o instanceof Index) {
      return (Index) o;
    } else {
      return new Index();
    }
  }

  public void put(List<String> uris, String source, CajoledModule cajoled) {
    put(uris.toArray(new String[uris.size()]), source, cajoled);
  }

  public void put(String[] uris, String source, CajoledModule cajoled) {
    Entry entry = new Entry(uris, source, cajoled);
    byte[] serial = serialize(entry);
    for (int k = 0; k < uris.length; k++) {
      index.map.put(normalizeUri(uris[k]), entry.id);
    }
    save(entry.id, serial);
  }

  public long getModTime() {
    return index.modTime;
  }

  public void setModTime(long millitime) {
    index.modTime = millitime;
  }

  public void finish() {
    save(INDEX_NAME, serialize(index));
  }

  @Override
  public CajoledModule lookupUri(String uri, boolean minify) {
    Entry e = Entry.from(load(idForUri(uri)));
    if (e != null && e.uris != null) {
      for (int k = 0; k < e.uris.length; k++) {
        if (uri.equals(e.uris[k])) {
          return minify ? e.minified : e.pretty;
        }
      }
    }
    return null;
  }

  @Override
  public CajoledModule lookupSource(String source, boolean minify) {
    source = normalizeSource(source);
    Entry e = Entry.from(load(idForSource(source)));
    if (e != null && source.equals(e.source)) {
      return minify ? e.minified : e.pretty;
    }
    return null;
  }

  public List<List<String>> getUrlGroups() {
    Map<String, List<String>> idMap = Maps.newHashMap();
    for (String url : index.map.keySet()) {
      String id = index.map.get(url);
      List<String> urls = idMap.get(id);
      if (urls == null) {
        urls = Lists.newArrayList();
        idMap.put(id, urls);
      }
      urls.add(url);
    }
    List<List<String>> urlGroups = Lists.newArrayList();
    for (List<String> urls : idMap.values()) {
      urlGroups.add(urls);
    }
    return urlGroups;
  }

  public static String normalizeUri(String uri) {
    try {
      URI u = new URI(uri).normalize();
      if (u.getHost() != null) {
        u = new URI(
            lowercase(u.getScheme()),
            u.getUserInfo(),
            lowercase(u.getHost()),
            u.getPort(),
            u.getPath(),
            u.getQuery(),
            u.getFragment());
      } else if (u.getScheme() != null) {
        u = new URI(
            lowercase(u.getScheme()),
            u.getSchemeSpecificPart(),
            u.getFragment());
      }
      return u.toString();
    } catch (URISyntaxException e) {
      return uri;
    }
  }

  private static String lowercase(String s) {
    return s == null ? null : Strings.lower(s);
  }

  private void save(String id, byte[] data) {
    try {
      new File(dir).mkdirs();
      FileOutputStream o = new FileOutputStream(new File(dir, id));
      o.write(data);
      o.close();
    } catch (IOException e) {
      throw new SomethingWidgyHappenedError(e);
    }
  }

  private String idForUri(String uri) {
    return index.map.get(normalizeUri(uri));
  }

  private static String idForSource(String source) {
    return computeHash(source);
  }

  private static String normalizeSource(String source) {
    // TODO(felix8a): I'd like to minify js here, but minifier is too slow
    return source.trim();
  }

  private byte[] load(String id) {
    byte[] result = loadResource(id);
    return result != null ? result : loadFile(id);
  }

  private byte[] loadFile(String id) {
    if (id == null) {
      return null;
    }
    try {
      return Files.toByteArray(new File(dir, id));
    } catch (IOException e) {
      return null;
    }
  }

  private byte[] loadResource(String id) {
    if (id == null) {
      return null;
    }
    ClassLoader cl = StaticPrecajoleMap.class.getClassLoader();
    InputStream is = cl.getResourceAsStream(dir + id);
    if (is == null) {
      return null;
    }
    try {
      return ByteStreams.toByteArray(is);
    } catch (IOException e) {
      return null;
    }
  }

  private static String computeHash(String s) {
    try {
      MessageDigest sha1 = MessageDigest.getInstance("SHA1");
      byte[] digest = sha1.digest(s.getBytes("UTF-8"));
      return new String(Hex.encodeHex(digest));
    } catch (NoSuchAlgorithmException e) {
      throw new SomethingWidgyHappenedError(e);
    } catch (UnsupportedEncodingException e) {
      throw new SomethingWidgyHappenedError(e);
    }
  }

  // TODO(felix8a): protobuf is much faster
  private static byte[] serialize(Object obj) {
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    try {
      ObjectOutputStream ostr = new ObjectOutputStream(buf);
      ostr.writeObject(obj);
      ostr.close();
    } catch (IOException e) {
      throw new SomethingWidgyHappenedError(e);
    }
    return buf.toByteArray();
  }

  private static Object deserialize(byte[] serial) {
    if (serial == null) {
      return null;
    }
    try {
      ByteArrayInputStream b = new ByteArrayInputStream(serial);
      ObjectInputStream i = new ObjectInputStream(b);
      return i.readObject();
    } catch (IOException e) {
      return null;
    } catch (ClassNotFoundException e) {
      return null;
    }
  }
}
TOP

Related Classes of com.google.caja.precajole.StaticPrecajoleMap$Index

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.