/*
 * Copyright 2018 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.passes;

import com.google.common.base.Predicate;
import com.google.template.soy.base.internal.IdGenerator;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.soytree.HtmlOpenTagNode;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.SoyNode;
import com.google.template.soy.soytree.SoyNode.Kind;
import com.google.template.soy.soytree.TemplateElementNode;
import com.google.template.soy.soytree.TemplateNode;

/** Validates restrictions specific to Soy elements. */
final class SoyElementPass extends CompilerFilePass {

  private static final SoyErrorKind ROOT_HAS_KEY_ATTR =
      SoyErrorKind.of(
          "The root node of Soy elements must not have a `key` attribute. "
              + "Instead, consider wrapping the Soy element in a keyed tag node.");

  private static final SoyErrorKind ROOT_IS_DYNAMIC_TAG =
      SoyErrorKind.of("The root node of Soy elements must not be a dynamic HTML tag.");

  private final ErrorReporter errorReporter;

  SoyElementPass(ErrorReporter errorReporter) {
    this.errorReporter = errorReporter;
  }

  @Override
  public void run(SoyFileNode file, IdGenerator nodeIdGen) {
    for (TemplateNode template : file.getChildren()) {
      if (!(template instanceof TemplateElementNode)) {
        continue;
      }

      HtmlOpenTagNode firstOpenTagNode =
          (HtmlOpenTagNode)
              template.firstChildThatMatches(
                  new Predicate<SoyNode>() {
                    @Override
                    public boolean apply(SoyNode node) {
                      return node.getKind() == Kind.HTML_OPEN_TAG_NODE;
                    }
                  });
      if (firstOpenTagNode == null) {
        // A prior pass will report an error if the Soy element has no open tag node,
        // so just skip in this case.
        continue;
      }

      validateNoKeyAttribute(firstOpenTagNode);
      validateNoDynamicTag(firstOpenTagNode);
    }
  }

  // See go/soy-element-keyed-roots for reasoning for why this is disallowed.
  private void validateNoKeyAttribute(HtmlOpenTagNode firstTagNode) {
    if (firstTagNode.getDirectAttributeNamed("key") != null) {
      errorReporter.report(firstTagNode.getSourceLocation(), ROOT_HAS_KEY_ATTR);
    }
  }

  private void validateNoDynamicTag(HtmlOpenTagNode firstTagNode) {
    if (!firstTagNode.getTagName().isStatic()) {
      errorReporter.report(firstTagNode.getSourceLocation(), ROOT_IS_DYNAMIC_TAG);
    }
  }
}

@@ -884,24 +884,72 @@
 penTagNode;%0A
+import com.google.template.soy.soytree.KeyNode;%0A
 import com.g
@@ -1363,20 +1363,20 @@
 HAS_KEY_
-ATTR
+NODE
  =%0A     
@@ -1454,23 +1454,11 @@
 e a 
-%60
 key
-%60 attribute
 . %22%0A
@@ -2610,25 +2610,16 @@
 ateNoKey
-Attribute
 (firstOp
@@ -2626,24 +2626,24 @@
 enTagNode);%0A
+
       valida
@@ -2737,19 +2737,18 @@
 asoning 
-for
+on
  why thi
@@ -2792,25 +2792,16 @@
 ateNoKey
-Attribute
 (HtmlOpe
@@ -2833,67 +2833,99 @@
     
-if (firstTagNode.getDirectAttributeNamed(%22key%22) != null
+for (SoyNode child : firstTagNode.getChildren()) %7B%0A      if (child instanceof KeyNode
 ) %7B%0A
+  
     
@@ -2998,14 +2998,22 @@
 KEY_
-ATTR);
+NODE);%0A      %7D
 %0A   

Modify SoyElementPass to report an error for element roots with key nodes, not key attributes.