/**
  * Appends a getter method for the text of html tag which contains the variable specified by the
  * name into the given string builder.
  *
  * @param methodBuilder {@link StringBuilder} the generated method will be appended to
  * @param uniqueVariableName the unique variable name with a sequential number
  * @param tagInfo the information of the html tag containing the template variable
  * @param varInfo the information of the template variable
  * @param isRepeated the boolean whether the specified html tag appears in a repeated part
  */
 private void appendTextGetter(
     StringBuilder methodBuilder,
     String uniqueVariableName,
     HtmlTagInfo tagInfo,
     VariableInfo varInfo,
     boolean isRepeated) {
   // TODO(kazuu): Help to select proper one from getFoo, getFoo2, getFoo3 ...
   appendLine(methodBuilder);
   String methodNamePrefix = !isRepeated ? "String getTextOf" : "List<String> getTextsOf";
   String foundedProcess =
       !isRepeated ? "return matcher.group(3);" : "result.add(matcher.group(3));";
   String finalProcess = !isRepeated ? "return null;" : "return result;";
   appendLine(
       methodBuilder,
       1,
       String.format(
           "public %s%s() {", methodNamePrefix, StringUtils.capitalize(uniqueVariableName)));
   if (isRepeated) {
     appendLine(methodBuilder, 2, "List<String> result = new ArrayList<String>();");
   }
   appendLine(
       methodBuilder, 2, "Matcher matcher = commentPattern.matcher(driver.getPageSource());");
   appendLine(methodBuilder, 2, "while (matcher.find()) {");
   appendLine(
       methodBuilder,
       3,
       String.format(
           "if (matcher.group(1).equals(\"%s\") && matcher.group(2).equals(\"%s\")) {",
           tagInfo.getAttributeValue(), varInfo.getName()));
   appendLine(methodBuilder, 4, foundedProcess);
   appendLine(methodBuilder, 3, "}");
   appendLine(methodBuilder, 2, "}");
   appendLine(methodBuilder, 2, finalProcess);
   appendLine(methodBuilder, 1, "}");
 }
  /**
   * Appends the body of skeleton test code, that is, only html tag fields and getter methods to
   * retrieve the values of the variables into the given string builder.
   *
   * @param builder {@link StringBuilder} the generated test code will be appended to
   * @param templateInfo the {@link TemplateInfo} of the template whose skeleton test code we want
   *     to generate
   */
  private void appendFieldsAndGetters(StringBuilder builder, TemplateInfo templateInfo) {
    // Create new StringBuilder to separate methods group from fields group such
    // as "private int field1; private int field2;
    // private void method1() {} private void method2() {}".
    StringBuilder methodBuilder = new StringBuilder();
    HashMultiset<String> varNameCounter = HashMultiset.create();

    appendLine(
        builder,
        1,
        "private static Pattern commentPattern = Pattern.compile(\"<!--POGEN,([^,]*),([^,]*),(.*?)-->\", Pattern.DOTALL);");

    for (HtmlTagInfo tagInfo : templateInfo.getHtmlTagInfos()) {
      // Skip this variable if it has no parent html tag
      // TODO(kazuu): Deal with these variable with a more proper way
      if (!tagInfo.hasParentTag()) {
        continue;
      }

      String attrValue = tagInfo.getAttributeValue();
      boolean isRepeated = tagInfo.isRepeated();

      for (VariableInfo varInfo : tagInfo.getVariableInfos()) {
        // When the same template variable appears in other html tags,
        // varIndex > 1 is satisfied
        varNameCounter.add(varInfo.getName());
        int varIndex = varNameCounter.count(varInfo.getName());
        String newVarName = varInfo.getName() + convertToString(varIndex);

        appendElementGetter(builder, methodBuilder, newVarName, attrValue, isRepeated);
        if (!varInfo.isManipulableTag()) {
          appendTextGetter(methodBuilder, newVarName, tagInfo, varInfo, isRepeated);
        }
        for (String attrName : varInfo.getSortedAttributeNames()) {
          appendAttributeGetter(methodBuilder, newVarName, attrName, attrValue, isRepeated);
        }
      }
    }
    // Append method definitions after field definitions
    builder.append(methodBuilder);
  }