All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.VariableMap Maven / Gradle / Ivy

Go to download

Closure Compiler is a JavaScript optimizing compiler. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. It is used in many of Google's JavaScript apps, including Gmail, Google Web Search, Google Maps, and Google Docs.

There is a newer version: v20240317
Show newest version
/*
 * Copyright 2005 The Closure Compiler Authors.
 *
 * 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.javascript.jscomp;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Map.Entry.comparingByKey;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.io.Files;
import com.google.errorprone.annotations.Immutable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.io.Writer;
import java.text.ParseException;
import java.util.Map;

/**
 * Stores the mapping from original variable name to new variable names.
 *
 * @see RenameVars
 */
@Immutable
public final class VariableMap implements Serializable {

  private static final char SEPARATOR = ':';

  /** Maps between original source name to new name */
  private final ImmutableBiMap map;

  public VariableMap(Map map) {
    this.map = ImmutableBiMap.copyOf(map);
  }

  /**
   * Given an original variable name, look up new name, may return null
   * if it's not found.
   */
  public String lookupNewName(String sourceName) {
    return map.get(sourceName);
  }

  /**
   * Given a new variable name, lookup the source name, may return null
   * if it's not found.
   */
  public String lookupSourceName(String newName) {
    return map.inverse().get(newName);
  }

  /** Returns an immutable mapping from original names to new names. */
  public ImmutableMap getOriginalNameToNewNameMap() {
    return ImmutableSortedMap.copyOf(map);
  }

  /** Returns an immutable mapping from new names to original names. */
  public ImmutableMap getNewNameToOriginalNameMap() {
    return map.inverse();
  }

  /**
   * Saves the variable map to a file.
   */
  @GwtIncompatible("com.google.io.Files")
  public void save(String filename) throws IOException {
    Files.write(toBytes(), new File(filename));
  }

  /**
   * Reads the variable map from a file written via {@link #save(String)}.
   */
  @GwtIncompatible("java.io.File")
  public static VariableMap load(String filename) throws IOException {
    try {
      return fromBytes(Files.toByteArray(new File(filename)));
    } catch (ParseException e) {
      // Wrap parse exception for backwards compatibility.
      throw new IOException(e);
    }
  }

  /**
   * Serializes the variable map to a byte array.
   */
  @GwtIncompatible("java.io.ByteArrayOutputStream")
  public byte[] toBytes() {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Writer writer = new OutputStreamWriter(baos, UTF_8);
    try {
      // The output order should be stable.
      for (Map.Entry entry :
          ImmutableSortedSet.copyOf(comparingByKey(), map.entrySet())) {
        writer.write(escape(entry.getKey()));
        writer.write(SEPARATOR);
        writer.write(escape(entry.getValue()));
        writer.write('\n');
      }
      writer.close();
    } catch (IOException e) {
      // Note: A ByteArrayOutputStream never throws IOException. This try/catch
      // is just here to appease the Java compiler.
      throw new RuntimeException(e);
    }
    return baos.toByteArray();
  }

  /**
   * Deserializes the variable map from a byte array returned by
   * {@link #toBytes()}.
   */
  @GwtIncompatible("com.google.common.base.Splitter.onPattern()")
  public static VariableMap fromBytes(byte[] bytes) throws ParseException {
    String string = new String(bytes, UTF_8);
    ImmutableMap.Builder map = ImmutableMap.builder();
    int startOfLine = 0;
    while (startOfLine < string.length()) {
      int newLine = string.indexOf('\n', startOfLine);
      if (newLine == -1) {
        newLine = string.length();
      }
      int endOfLine = newLine;
      if (string.charAt(newLine - 1) == '\r') {
        newLine--;
      }
      String line = string.substring(startOfLine, newLine);
      startOfLine = endOfLine + 1; // update index for next iteration
      if (line.isEmpty()) {
        continue;
      }
      int pos = findIndexOfUnescapedChar(line, SEPARATOR);
      if (pos <= 0) {
        throw new ParseException("Bad line: " + line, 0);
      }
      map.put(
          unescape(line.substring(0, pos)),
          pos == line.length() - 1 ? "" : unescape(line.substring(pos + 1)));
    }
    return new VariableMap(map.buildOrThrow());
  }

  private static String escape(String value) {
    return value.replace("\\", "\\\\")
        .replace(":", "\\:")
        .replace("\n", "\\n");
  }

  private static int findIndexOfUnescapedChar(String value, char stopChar) {
    int len = value.length();
    for (int i = 0; i < len; ) {
      int stopCharIndex = value.indexOf(stopChar, i);
      if (stopCharIndex == -1) {
        return -1;
      }
      if (value.charAt(stopCharIndex - 1) != '\\') {
        // it isn't escaped, return
        return stopCharIndex;
      }
      i = stopCharIndex + 1;
    }
    return -1;
  }

  private static String unescape(String value) {
    int slashIndex = value.indexOf('\\');
    if (slashIndex == -1) {
      return value;
    }
    StringBuilder sb = new StringBuilder(value.length() - 1);
    sb.append(value, 0, slashIndex);
    int len = value.length();
    for (int i = slashIndex; i < len; i++) {
      char c = value.charAt(i);
      if (c == '\\' && ++i < len) {
        c = value.charAt(i);
      }
      sb.append(c);
    }
    return sb.toString();
  }

  /**
   * Initializes the variable map from an existing map.
   * @param map The map to use from original names to generated names. It is
   *   copied and changes to the specified map will not affect the returned
   *   object.
   */
  public static VariableMap fromMap(Map map) {
    return new VariableMap(map);
  }

  @VisibleForTesting
  ImmutableMap toMap() {
    return map;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy