Package com.google.caja.parser.quasiliteral

Source Code of com.google.caja.parser.quasiliteral.NameContext$VarInfo

// Copyright (C) 2009 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.parser.quasiliteral;

import com.google.caja.CajaException;
import com.google.caja.lexer.FilePosition;
import com.google.caja.reporting.Message;
import com.google.caja.reporting.MessagePart;
import com.google.caja.util.Maps;

import java.util.Collections;
import java.util.Iterator;
import java.util.Map;

/**
* A mapping of identifiers which ensures that no use of an identifier in a
* scope masks a use of an identifier in an outer scope.
* <p>
* Javascript is not a block scoped language, but IHTML constructs are block
* scoped, so we alpha-rename all variables to prevent collisions.
*
* @param <NAME> a type that can work as a hashtable key
* @param <BINDING> type of data associated with a particular name.
*    This can be any object related to the result of resolving the name.
*
* @author mikesamuel@gmail.com
*/
public final class NameContext<NAME, BINDING> {
  private final NameContext<NAME, BINDING> parent;
  private final Iterator<String> nameGenerator;
  /** maps names in original source to details about the renamed instance. */
  private final Map<NAME, VarInfo<NAME, BINDING>> vars
      = Maps.newLinkedHashMap();

  public static final class VarInfo<NAME, BINDING> {
    public final NAME origName;
    public final String newName;
    public final FilePosition declaredAt;
    private BINDING binding;

    private VarInfo(NAME origName, String newName, FilePosition declaredAt) {
      assert origName != null;
      this.origName = origName;
      this.newName = newName;
      this.declaredAt = declaredAt;
    }

    public BINDING getBinding() { return binding; }
    public void bind(BINDING binding) { this.binding = binding; }

    @Override
    public String toString() {
      return "(" + getClass().getSimpleName() + " " + origName + ")";
    }
  }

  /**
   * Creates a context with no parent context.
   * @param nameGenerator an infinite iterator that returns safe identifiers
   *    and that never returns the same String twice.
   *    Typically, a {@link com.google.caja.util.SafeIdentifierMaker}.
   */
  public NameContext(Iterator<String> nameGenerator) {
    this(null, nameGenerator);
  }

  private NameContext(
      NameContext<NAME, BINDING> parent, Iterator<String> nameGenerator) {
    this.parent = parent;
    this.nameGenerator = nameGenerator;
  }

  /**
   * Produces a context that has the same name generator and which has this
   * context as its parent.
   */
  public NameContext<NAME, BINDING> makeChildContext() {
    return new NameContext<NAME, BINDING>(this, this.nameGenerator);
  }

  /**
   * The context that is used to resolve original names that have not been
   * declared in this context, or null if no such context.
   */
  public NameContext<NAME, BINDING> getParentContext() { return parent; }

  /**
   * Introduce a new declaration which will mask any declaration with the same
   * name in the {@link #getParentContext} context.
   */
  public VarInfo<NAME, BINDING> declare(NAME origName, FilePosition declSite)
      throws RedeclarationException {
    VarInfo<NAME, BINDING> d = vars.get(origName);
    if (d == null) {
      String newName = nameGenerator.next();
      VarInfo<NAME, BINDING> vi = new VarInfo<NAME, BINDING>(
          origName, newName, declSite);
      vars.put(origName, vi);
      return vi;
    } else {
      FilePosition dPos = d.declaredAt;
      throw new RedeclarationException(new Message(
          RewriterMessageType.CANNOT_REDECLARE_VAR, declSite,
          MessagePart.Factory.valueOf(origName.toString()), dPos));
    }
  }

  /**
   * Find a declaration with the given original name, looking in ancestor
   * contexts if {@code declare(originalName, ...)} was never called on this
   * context.
   */
  public VarInfo<NAME, BINDING> lookup(NAME originalName) {
    for (NameContext<NAME, BINDING> c = this; c != null; c = c.parent) {
      VarInfo<NAME, BINDING> vi = c.vars.get(originalName);
      if (vi != null) { return vi; }
    }
    return null;
  }

  /**
   * The set of vars declared in this context, not including any in ancestor
   * contexts.
   */
  public Iterable<VarInfo<NAME, BINDING>> vars() {
    return Collections.unmodifiableMap(vars).values();
  }

  /**
   * The name generator used to generate names for new declarations.
   */
  public Iterator<String> getNameGenerator() { return nameGenerator; }

  public static class RedeclarationException extends CajaException {
    private static final long serialVersionUID = -1818955396015380126L;
    public RedeclarationException(Message m, Throwable th) { super(m, th); }
    public RedeclarationException(Message m) { this(m, null); }
  }
}
TOP

Related Classes of com.google.caja.parser.quasiliteral.NameContext$VarInfo

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.