/*
 * Copyright 2010 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.sharedpasses;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.template.soy.sharedpasses.FindTransitiveDepTemplatesVisitor.TransitiveDepTemplatesInfo;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.SoyFileSetNode;
import com.google.template.soy.soytree.SoytreeUtils;
import com.google.template.soy.soytree.TemplateNode;
import com.google.template.soy.soytree.TemplateRegistry;

import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;


/**
 * Visitor for finding the injected params used by a given template.
 *
 * <p> Important: Do not use outside of Soy code (treat as superpackage-private).
 *
 * <p> {@link #exec} should be called on a {@code TemplateNode}.
 *
 * <p> If you need to call this visitor for multiple templates in the same tree (without modifying
 * the tree), it's more efficient to reuse the same instance of this visitor because we memoize
 * results from previous calls to exec.
 *
 */
public class FindIjParamsVisitor {


  /**
   * Return value for {@code FindIjParamsVisitor}.
   */
  public static class IjParamsInfo {


    /** Sorted set of inject params (i.e. the keys of the multimap below). */
    public final ImmutableSortedSet<String> ijParamSet;

    /** Multimap from injected param key to transitive callees that use the param. */
    public final ImmutableMultimap<String, TemplateNode> ijParamToCalleesMultimap;

    /** Whether the template (that the pass was run on) may have injected params indirectly used in
     *  external basic calls. */
    public final boolean mayHaveIjParamsInExternalCalls;

    /** Whether the template (that the pass was run on) may have injected params indirectly used in
     *  external delegate calls. */
    public final boolean mayHaveIjParamsInExternalDelCalls;


    /**
     * @param ijParamToCalleesMultimap Multimap from injected param key to transitive callees that
     *     use the param.
     * @param mayHaveIjParamsInExternalCalls Whether the template (that the pass was run on) may
     *     have injected params indirectly used in external basic calls.
     * @param mayHaveIjParamsInExternalDelCalls Whether the template (that the pass was run on) may
     *     have injected params indirectly used in external delegate calls.
     */
    public IjParamsInfo(
        ImmutableMultimap<String, TemplateNode> ijParamToCalleesMultimap,
        boolean mayHaveIjParamsInExternalCalls, boolean mayHaveIjParamsInExternalDelCalls) {
      this.ijParamToCalleesMultimap = ijParamToCalleesMultimap;
      this.ijParamSet = ImmutableSortedSet.copyOf(ijParamToCalleesMultimap.keySet());
      this.mayHaveIjParamsInExternalCalls = mayHaveIjParamsInExternalCalls;
      this.mayHaveIjParamsInExternalDelCalls = mayHaveIjParamsInExternalDelCalls;
    }

  }


  // -----------------------------------------------------------------------------------------------
  // FindIjParamsVisitor body.


  /** The FindTransitiveDepTemplatesVisitor to use. */
  private final FindTransitiveDepTemplatesVisitor findTransitiveDepTemplatesVisitor;

  /** Map from TransitiveDepTemplatesInfo to IjParamsInfo, containing memoized results that were
   * computed in previous calls to exec. */
  private final Map<TransitiveDepTemplatesInfo, IjParamsInfo> depsInfoToIjParamsInfoMap;

  /** Map from template to set of ij params used locally in that template, containing memoized
   *  results that were found in previous calls to exec. */
  private final Map<TemplateNode, Set<String>> templateToLocalIjParamsMap;


  /**
   * @param templateRegistry Map from template name to TemplateNode to use during the pass.
   */
  public FindIjParamsVisitor(@Nullable TemplateRegistry templateRegistry) {
    this.findTransitiveDepTemplatesVisitor =
        new FindTransitiveDepTemplatesVisitor(templateRegistry);
    depsInfoToIjParamsInfoMap = Maps.newHashMap();
    templateToLocalIjParamsMap = Maps.newHashMap();
  }


  /**
   * Computes injected params info for a template.
   *
   * <p> Note: This method is not thread-safe. If you need to get injected params info in a
   * thread-safe manner, then please use {@link #execOnAllTemplates}() in a thread-safe manner.
   */
  public IjParamsInfo exec(TemplateNode rootTemplate) {

    TransitiveDepTemplatesInfo depsInfo = findTransitiveDepTemplatesVisitor.exec(rootTemplate);

    if (! depsInfoToIjParamsInfoMap.containsKey(depsInfo)) {

      ImmutableMultimap.Builder<String, TemplateNode> ijParamToCalleesMultimapBuilder =
          ImmutableMultimap.builder();

      for (TemplateNode template : depsInfo.depTemplateSet) {

        if (! templateToLocalIjParamsMap.containsKey(template)) {
          FindIjParamsInExprHelperVisitor helperVisitor = new FindIjParamsInExprHelperVisitor();
          SoytreeUtils.execOnAllV2Exprs(template, helperVisitor);
          Set<String> localIjParams = helperVisitor.getResult();
          templateToLocalIjParamsMap.put(template, localIjParams);
        }

        for (String localIjParam : templateToLocalIjParamsMap.get(template)) {
          ijParamToCalleesMultimapBuilder.put(localIjParam, template);
        }
      }

      IjParamsInfo ijParamsInfo = new IjParamsInfo(
          ijParamToCalleesMultimapBuilder.build(), depsInfo.hasExternalCalls, depsInfo.hasDelCalls);
      depsInfoToIjParamsInfoMap.put(depsInfo, ijParamsInfo);
    }

    return depsInfoToIjParamsInfoMap.get(depsInfo);
  }


  /**
   * Precomputes injected params info for all templates.
   *
   * <p> Note: This method is not thread-safe. If you need to get injected params info in a
   * thread-safe manner, be sure to call this method only once and then use the precomputed map.
   *
   * @param soyTree The full Soy tree.
   * @return A map from template node to injected params info for all templates. The returned map
   *     is deeply immutable ({@code IjParamsInfo} is immutable).
   */
  public ImmutableMap<TemplateNode, IjParamsInfo> execOnAllTemplates(SoyFileSetNode soyTree) {

    ImmutableMap.Builder<TemplateNode, IjParamsInfo> resultMapBuilder = ImmutableMap.builder();

    for (SoyFileNode soyFile : soyTree.getChildren()) {
      for (TemplateNode template : soyFile.getChildren()) {
        resultMapBuilder.put(template, exec(template));
      }
    }

    return resultMapBuilder.build();
  }

}

@@ -1202,16 +1202,75 @@
 egistry;
+%0Aimport com.google.template.soy.soytree.defn.TemplateParam;
 %0A%0Aimport
@@ -5975,24 +5975,189 @@
 );%0A        %7D
+%0A%0A        for (TemplateParam injectedParam : template.getInjectedParams()) %7B%0A          ijParamToCalleesMultimapBuilder.put(injectedParam.name(), template);%0A        %7D
 %0A      %7D%0A%0A  

Find $ij params declared with the new {@inject ...} syntax in Soy. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=80846485