Add parameter type to method documentation.
This rewrites the paramTags() method to use just one loop based
on the ParameterInfo for each method parameter. That is, for each
parameter identified from the method signature, collect the name
and type... then compare that name to the names in the @param tags.
If one matches, then collect that @param documentation.

This is the inverse of the way it used to be, which collected the
parameter documentation based soley on what was given in the @param
tags. So any parameters without the @param tag would simply not be
documented in the output... Now, all parameters get listed with their
name and type. The comments from @param are considered a bonus.

Unfortunately, this does not create links to the parameter type.
That will require a bit more work to change the way the data gets
written to the HDF data. So I decided to do this part as an incremental
improvement. Another revision to allow linking to data types will come later.

Change-Id: I32b26ba53c0637d4f5c04eee67bf515e0004a529
diff --git a/res/assets/templates/macros.cs b/res/assets/templates/macros.cs
index 7c2a8d2..ff6e3be 100644
--- a/res/assets/templates/macros.cs
+++ b/res/assets/templates/macros.cs
@@ -268,28 +268,15 @@
           /if ?><?cs var:param.name ?><?cs
           if:param.isTypeParameter ?>&gt;<?cs
           /if ?></code></td>
-        <td width="100%"><?cs call:tag_list(param.comment) ?></td>
+        <td width="100%">
+          <code><?cs var:param.kind ?></code><?cs
+          if:string.find(param.comment.0.text, "<!--") != 0
+            ?>:<?cs # Do not print if param comment is an HTML comment ?><?cs
+          /if ?>
+          <?cs call:tag_list(param.comment) ?></td>
       </tr><?cs
     /each ?>
     </table><?cs
-  #
-  # If no param tags found, but there are known params, print those instead
-  #
-  ?><?cs
-  elif:subcount(obj.params) ?>
-    <table class="responsive">
-      <tr><th colspan=2>Parameters</th></tr><?cs
-      each:param = obj.params ?>
-        <tr>
-          <td><code><?cs
-            if:param.isTypeParameter ?>&lt;<?cs
-            /if ?><?cs var:param.name ?><?cs
-            if:param.isTypeParameter ?>&gt;<?cs
-            /if ?></code></td>
-          <td width="100%"><!-- no param description in source --></td>
-        </tr><?cs
-      /each ?>
-    </table><?cs
   /if ?><?cs
   #
   # Print the @return value
diff --git a/src/com/google/doclava/MethodInfo.java b/src/com/google/doclava/MethodInfo.java
index 5dc217c..f2f040e 100644
--- a/src/com/google/doclava/MethodInfo.java
+++ b/src/com/google/doclava/MethodInfo.java
@@ -227,7 +227,7 @@
     }
     return mIsDeprecated;
   }
-  
+
   public void setDeprecated(boolean deprecated) {
     mDeprecatedKnown = true;
     mIsDeprecated = deprecated;
@@ -329,7 +329,7 @@
   public TypeInfo returnType() {
     return mReturnType;
   }
-  
+
   public String prettySignature() {
     return name() + prettyParameters();
   }
@@ -337,7 +337,7 @@
   public String prettyQualifiedSignature() {
     return qualifiedName() + prettyParameters();
   }
-  
+
   /**
    * Returns a printable version of the parameters of this method's signature.
    */
@@ -349,7 +349,7 @@
       }
       params.append(pInfo.type().simpleTypeName());
     }
-    
+
     params.append(")");
     return params.toString();
   }
@@ -415,79 +415,97 @@
     return mThrowsTags;
   }
 
-  private static int indexOfParam(String name, String[] list) {
+  private static int indexOfParam(String name, ParamTagInfo[] list) {
     final int N = list.length;
     for (int i = 0; i < N; i++) {
-      if (name.equals(list[i])) {
+      if (name.equals(list[i].parameterName())) {
         return i;
       }
     }
     return -1;
   }
 
+  /* Checks whether the name documented with the provided @param tag
+   * actually matches one of the method parameters. */
+  private boolean isParamTagInMethod(ParamTagInfo tag) {
+    for (ParameterInfo paramInfo : mParameters) {
+      if (paramInfo.name().equals(tag.parameterName())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public ParamTagInfo[] paramTags() {
     if (mParamTags == null) {
       final int N = mParameters.size();
+      final String DEFAULT_COMMENT = "<!-- no parameter comment -->";
 
       if (N == 0) {
           // Early out for empty case.
           mParamTags = ParamTagInfo.EMPTY_ARRAY;
           return ParamTagInfo.EMPTY_ARRAY;
       }
+      // Where we put each result
+      mParamTags = ParamTagInfo.getArray(N);
 
-      String[] names = new String[N];
-      String[] comments = new String[N];
-      SourcePositionInfo[] positions = new SourcePositionInfo[N];
+      // collect all the @param tag info
+      ParamTagInfo[] paramTags = comment().paramTags();
 
-      // get the right names so we can handle our names being different from
-      // our parent's names.
+      // Complain about misnamed @param tags
+      for (ParamTagInfo tag : paramTags) {
+        if (!isParamTagInMethod(tag)){
+          Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
+              "@param tag with name that doesn't match the parameter list: '"
+              + tag.parameterName() + "'");
+        }
+      }
+
+      // Loop the parameters known from the method signature...
+      // Start by getting the known parameter name and data type. Then, if
+      // there's an @param tag that matches the current parameter name, get the
+      // javadoc comments. But if there's no @param comments here, then
+      // check if it's available from the parent class.
       int i = 0;
       for (ParameterInfo param : mParameters) {
-        names[i] = param.name();
-        comments[i] = "";
-        positions[i] = param.position();
-        i++;
-      }
+        String name = param.name();
+        String type = param.type().simpleTypeName();
+        String comment = DEFAULT_COMMENT;
+        SourcePositionInfo position = param.position();
 
-      // gather our comments, and complain about misnamed @param tags
-      for (ParamTagInfo tag : comment().paramTags()) {
-        int index = indexOfParam(tag.parameterName(), names);
+        // Find the matching param from the @param tags in order to get
+        // the parameter comments
+        int index = indexOfParam(name, paramTags);
         if (index >= 0) {
-          comments[index] = tag.parameterComment();
-          positions[index] = tag.position();
-        } else {
-          Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
-              "@param tag with name that doesn't match the parameter list: '" + tag.parameterName()
-                  + "'");
+          comment = paramTags[index].parameterComment();
+          position = paramTags[index].position();
         }
-      }
 
-      // get our parent's tags to fill in the blanks
-      MethodInfo overridden = this.findOverriddenMethod(name(), signature());
-      if (overridden != null) {
-        ParamTagInfo[] maternal = overridden.paramTags();
-        for (i = 0; i < N; i++) {
-          if (comments[i].equals("")) {
-            comments[i] = maternal[i].parameterComment();
-            positions[i] = maternal[i].position();
+        // get our parent's tags to fill in the blanks
+        MethodInfo overridden = this.findOverriddenMethod(name(), signature());
+        if (overridden != null) {
+          ParamTagInfo[] maternal = overridden.paramTags();
+          if (comment.equals(DEFAULT_COMMENT)) {
+            comment = maternal[i].parameterComment();
+            position = maternal[i].position();
           }
         }
-      }
 
-      // construct the results, and cache them for next time
-      mParamTags = ParamTagInfo.getArray(N);
-      for (i = 0; i < N; i++) {
+        // Okay, now add the collected parameter information to the method data
         mParamTags[i] =
-            new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
-                positions[i]);
+            new ParamTagInfo("@param", type, name + " " + comment, parent(),
+                position);
 
-        // while we're here, if we find any parameters that are still undocumented at this
-        // point, complain. (this warning is off by default, because it's really, really
-        // common; but, it's good to be able to enforce it)
-        if (comments[i].equals("")) {
-          Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i], "Undocumented parameter '"
-              + names[i] + "' on method '" + name() + "'");
+        // while we're here, if we find any parameters that are still
+        // undocumented at this point, complain. This warning is off by
+        // default, because it's really, really common;
+        // but, it's good to be able to enforce it.
+        if (comment.equals(DEFAULT_COMMENT)) {
+          Errors.error(Errors.UNDOCUMENTED_PARAMETER, position,
+              "Undocumented parameter '" + name + "' on method '"
+              + name() + "'");
         }
+        i++;
       }
     }
     return mParamTags;
@@ -536,7 +554,7 @@
         String s = params[i];
         int slen = s.length();
         int qnlen = qualifiedName.length();
-        
+
         // Check for a matching generic name or best known type
         if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
           return false;
@@ -546,7 +564,7 @@
     }
     return true;
   }
-  
+
   /**
    * Checks to see if a parameter from a method signature is
    * compatible with a parameter given in a {@code @link} tag.
@@ -692,13 +710,13 @@
   public String getReason() {
     return mReasonOpened;
   }
-  
+
   public void addException(String exec) {
     ClassInfo exceptionClass = new ClassInfo(exec);
 
     mThrownExceptions.add(exceptionClass);
   }
-  
+
   public void addParameter(ParameterInfo p) {
     // Name information
     if (mParameters == null) {
@@ -727,8 +745,8 @@
   private AnnotationValueInfo mDefaultAnnotationElementValue;
   private String mReasonOpened;
   private ArrayList<Resolution> mResolutions;
-  
-  // TODO: merge with droiddoc version (above)  
+
+  // TODO: merge with droiddoc version (above)
   public String qualifiedName() {
     String parentQName = (containingClass() != null)
         ? (containingClass().qualifiedName() + ".") : "";
@@ -747,7 +765,7 @@
         }
         params.append(pInfo.type().fullName());
       }
-      
+
       params.append(")");
       mSignature = params.toString();
     }
diff --git a/src/com/google/doclava/ParamTagInfo.java b/src/com/google/doclava/ParamTagInfo.java
index 13eb30b..d9353c5 100644
--- a/src/com/google/doclava/ParamTagInfo.java
+++ b/src/com/google/doclava/ParamTagInfo.java
@@ -75,6 +75,7 @@
   @Override
   public void makeHDF(Data data, String base) {
     data.setValue(base + ".name", parameterName());
+    data.setValue(base + ".kind", kind());
     data.setValue(base + ".isTypeParameter", isTypeParameter() ? "1" : "0");
     TagInfo.makeHDF(data, base + ".comment", commentTags());
   }