Package com.google.template.soy.soytree

Source Code of com.google.template.soy.soytree.TemplateRegistry$DelegateTemplateConflictException

/*
* Copyright 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.template.soy.soytree;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.template.soy.base.SoySyntaxException;
import com.google.template.soy.soytree.TemplateDelegateNode.DelTemplateKey;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.concurrent.Immutable;


/**
* A registry or index of all templates in a Soy tree.
*
* <p> Important: Do not use outside of Soy code (treat as superpackage-private).
*
* @author Kai Huang
*/
public class TemplateRegistry {


  /**
   * Represents a set of delegate templates with the same key (name and variant) and same priority.
   * <p> Note: Per delegate rules, at most one of the templates in each division may be active at
   * render time.
   *
   * <p> Important: Do not use outside of Soy code (treat as superpackage-private).
   */
  @Immutable
  public static class DelegateTemplateDivision {

    /** The priority value for the delegate templates in this division. */
    public final int delPriority;
    /** Map of all delegate templates in this division, by delegate package name. */
    public final Map<String, TemplateDelegateNode> delPackageNameToDelTemplateMap;

    public DelegateTemplateDivision(
        int delPriority, Map<String, TemplateDelegateNode> delPackageNameToDelTemplateMap) {
      this.delPriority = delPriority;
      this.delPackageNameToDelTemplateMap =
          Collections.unmodifiableMap(Maps.newHashMap(delPackageNameToDelTemplateMap));
    }
  }


  /**
   * Exception thrown when there's no unique highest-priority active delegate template at render
   * time.
   *
   * <p> Important: Do not use outside of Soy code (treat as superpackage-private).
   */
  public static class DelegateTemplateConflictException extends Exception {

    public DelegateTemplateConflictException(String errorMsg) {
      super(errorMsg);
    }
  }


  /** Map from basic template name to node. */
  private final Map<String, TemplateBasicNode> basicTemplatesMap;

  /** Map from delegate template name to set of keys. */
  private final Map<String, Set<DelTemplateKey>> delTemplateNameToKeysMap;

  /** Map from delegate template key to list of DelegateTemplateDivision, where the list is in
   *  descending priority order. */
  private final Map<DelTemplateKey, List<DelegateTemplateDivision>> delTemplatesMap;


  /**
   * Constructor.
   * @param soyTree The Soy tree from which to build a template registry.
   */
  public TemplateRegistry(SoyFileSetNode soyTree) {

    // ------ Iterate through all templates to collect data. ------

    Map<String, TemplateBasicNode> tempBasicTemplatesMap = Maps.newHashMap();
    Map<String, Set<DelTemplateKey>> tempDelTemplateNameToKeysMap = Maps.newHashMap();
    Map<DelTemplateKey, Map<Integer, Map<String, TemplateDelegateNode>>> tempDelTemplatesMap =
        Maps.newHashMap();

    for (SoyFileNode soyFile : soyTree.getChildren()) {
      for (TemplateNode template : soyFile.getChildren()) {

        if (template instanceof TemplateBasicNode) {
          // Case 1: Basic template.
          tempBasicTemplatesMap.put(template.getTemplateName(), (TemplateBasicNode) template);

        } else {
          // Case 2: Delegate template.
          TemplateDelegateNode delTemplate = (TemplateDelegateNode) template;
          DelTemplateKey delTemplateKey = delTemplate.getDelTemplateKey();

          // Add to tempDelTemplateNameToKeysMap.
          String delTemplateName = delTemplate.getDelTemplateName();
          Set<DelTemplateKey> keys = tempDelTemplateNameToKeysMap.get(delTemplateName);
          if (keys == null) {
            keys = Sets.newLinkedHashSet();
            tempDelTemplateNameToKeysMap.put(delTemplateName, keys);
          }
          keys.add(delTemplateKey);

          // Add to tempDelTemplatesMap.
          int delPriority = delTemplate.getDelPriority();
          String delPackageName = delTemplate.getDelPackageName();

          Map<Integer, Map<String, TemplateDelegateNode>> tempDivisions =
              tempDelTemplatesMap.get(delTemplateKey);
          if (tempDivisions == null) {
            tempDivisions = Maps.newHashMap();
            tempDelTemplatesMap.put(delTemplateKey, tempDivisions);
          }

          Map<String, TemplateDelegateNode> tempDivision = tempDivisions.get(delPriority);
          if (tempDivision == null) {
            tempDivision = Maps.newHashMap();
            tempDivisions.put(delPriority, tempDivision);
          }

          if (tempDivision.containsKey(delPackageName)) {
            TemplateDelegateNode prevTemplate = tempDivision.get(delPackageName);
            String prevTemplateFilePath =
                prevTemplate.getNearestAncestor(SoyFileNode.class).getFilePath();
            String currTemplateFilePath =
                delTemplate.getNearestAncestor(SoyFileNode.class).getFilePath();
            String errorMsgPrefix = (delPackageName == null) ?
                "Found two default implementations" :
                "Found two implementations in the same delegate package";
            if (currTemplateFilePath != null && currTemplateFilePath.equals(prevTemplateFilePath)) {
              throw SoySyntaxException.createWithoutMetaInfo(String.format(
                  errorMsgPrefix + " for delegate template '%s', both in the file %s.",
                  delTemplateKey, currTemplateFilePath));
            } else {
              throw SoySyntaxException.createWithoutMetaInfo(String.format(
                  errorMsgPrefix + " for delegate template '%s', in files %s and %s.",
                  delTemplateKey, prevTemplateFilePath, currTemplateFilePath));
            }
          }
          tempDivision.put(delPackageName, delTemplate);
        }
      }
    }

    // ------ Build the final data structures. ------

    basicTemplatesMap = Collections.unmodifiableMap(tempBasicTemplatesMap);

    ImmutableMap.Builder<DelTemplateKey, List<DelegateTemplateDivision>> delTemplatesMapBuilder =
        ImmutableMap.builder();

    for (DelTemplateKey delTemplateKey : tempDelTemplatesMap.keySet()) {
      Map<Integer, Map<String, TemplateDelegateNode>> tempDivisions =
          tempDelTemplatesMap.get(delTemplateKey);

      ImmutableList.Builder<DelegateTemplateDivision> divisionsBuilder = ImmutableList.builder();

      // Note: List should be in decreasing priority order.
      for (int priority = TemplateNode.MAX_PRIORITY; priority >= 0; priority--) {
        if (! tempDivisions.containsKey(priority)) {
          continue;
        }
        Map<String, TemplateDelegateNode> tempDivision = tempDivisions.get(priority);
        DelegateTemplateDivision division = new DelegateTemplateDivision(priority, tempDivision);
        divisionsBuilder.add(division);
      }

      delTemplatesMapBuilder.put(delTemplateKey, divisionsBuilder.build());
    }

    delTemplatesMap = delTemplatesMapBuilder.build();

    delTemplateNameToKeysMap = Collections.unmodifiableMap(tempDelTemplateNameToKeysMap);
  }


  /**
   * Returns a map from basic template name to node.
   */
  public Map<String, TemplateBasicNode> getBasicTemplatesMap() {
    return basicTemplatesMap;
  }


  /**
   * Retrieves a basic template given the template name.
   * @param templateName The basic template name to retrieve.
   * @return The corresponding basic template, or null if the template name is not defined.
   */
  public TemplateBasicNode getBasicTemplate(String templateName) {
    return basicTemplatesMap.get(templateName);
  }


  /**
   * Returns a map from delegate template name to set of keys.
   */
  public Map<String, Set<DelTemplateKey>> getDelTemplateNameToKeysMap() {
    return delTemplateNameToKeysMap;
  }


  /**
   * Returns a map from delegate template key (name and variant) to list of
   * {@code DelegateTemplateDivision}s, where each list is sorted in descending priority order.
   */
  public Map<DelTemplateKey, List<DelegateTemplateDivision>> getDelTemplatesMap() {
    return delTemplatesMap;
  }


  /**
   * Retrieves the set of key (name and variant) strings for all variants of a given delegate
   * template name.
   * @param delTemplateName The delegate template name to retrieve.
   * @return The set of keys for all variants.
   */
  public Set<DelTemplateKey> getDelTemplateKeysForAllVariants(String delTemplateName) {
    return delTemplateNameToKeysMap.get(delTemplateName);
  }


  /**
   * Retrieves the set of {@code DelegateTemplateDivision}s for all variants of a given a delegate
   * template name.
   * @param delTemplateName The delegate template name to retrieve.
   * @return The set of {@code DelegateTemplateDivision}s for all variants.
   */
  public Set<DelegateTemplateDivision> getDelTemplateDivisionsForAllVariants(
      String delTemplateName) {

    Set<DelTemplateKey> keysForAllVariants = delTemplateNameToKeysMap.get(delTemplateName);
    if (keysForAllVariants == null) {
      return null;
    }

    Set<DelegateTemplateDivision> divisionsForAllVariants = Sets.newLinkedHashSet();
    for (DelTemplateKey delTemplateKey : keysForAllVariants) {
      divisionsForAllVariants.addAll(delTemplatesMap.get(delTemplateKey));
    }
    return divisionsForAllVariants;
  }


  /**
   * Retrieves the list of {@code DelegateTemplateDivision}s (sorted in descencing priority order)
   * given a delegate template key (name and variant).
   * @param delTemplateKey The delegate template key (name and variant) to retrieve.
   * @return The corresponding list of {@code DelegateTemplateDivision}s (sorted in descencing
   *     priority order), or null if the delegate template key is not implemented.
   */
  public List<DelegateTemplateDivision> getSortedDelTemplateDivisions(
      DelTemplateKey delTemplateKey) {
    return delTemplatesMap.get(delTemplateKey);
  }


  /**
   * Selects a delegate template based on the rendering rules, given the delegate template key (name
   * and variant) and the set of active delegate package names.
   * @param delTemplateKey The delegate template key (name and variant) to select an implementation
   *     for.
   * @param activeDelPackageNames The set of active delegate package names.
   * @return The selected delegate template, or null if there are no active implementations.
   * @throws DelegateTemplateConflictException If there are two or more active implementations with
   *     equal priority (unable to select one over the other).
   */
  public TemplateDelegateNode selectDelTemplate(
      DelTemplateKey delTemplateKey, Set<String> activeDelPackageNames)
      throws DelegateTemplateConflictException {

    List<DelegateTemplateDivision> divisions = delTemplatesMap.get(delTemplateKey);
    if (divisions == null && delTemplateKey.variant.length() > 0) {
      // Fallback to empty variant.
      divisions = delTemplatesMap.get(new DelTemplateKey(delTemplateKey.name, ""));
    }
    if (divisions == null) {
      return null;
    }

    for (DelegateTemplateDivision division : divisions) {

      TemplateDelegateNode delTemplate = null;

      for (String delPackageName : division.delPackageNameToDelTemplateMap.keySet()) {
        if (delPackageName != null && ! activeDelPackageNames.contains(delPackageName)) {
          continue;
        }

        if (delTemplate != null) {
          throw new DelegateTemplateConflictException(String.format(
              "For delegate template '%s', found two active implementations with equal" +
                  " priority in delegate packages '%s' and '%s'.",
              delTemplateKey, delTemplate.getDelPackageName(), delPackageName));
        }
        delTemplate = division.delPackageNameToDelTemplateMap.get(delPackageName);
      }

      if (delTemplate != null) {
        return delTemplate;
      }
    }

    return null;
  }

}
TOP

Related Classes of com.google.template.soy.soytree.TemplateRegistry$DelegateTemplateConflictException

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.