Adapting ProblemReport.build to use a sublogger for child messages (e.g. the subtypes tried for an instantiable type).

Review by: spoon

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5269 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProblemReport.java b/user/src/com/google/gwt/user/rebind/rpc/ProblemReport.java
index 56b000b..848fc8d 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProblemReport.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProblemReport.java
@@ -44,9 +44,43 @@
    */

   public enum Priority { FATAL, DEFAULT, AUXILIARY}

 

-  private Map<JClassType, List<String>> allProblems;

-  private Map<JClassType, List<String>> auxiliaries;

-  private Map<JClassType, List<String>> fatalProblems;

+  /**

+   * An individual report, which may require multiple entries (expressed as

+   * logs under a branchpoint), but relates to an individual issue.

+   */

+  public class Problem {

+    private String message;

+    private List<String> childMessages;

+

+    private Problem(String message, String[] children) {

+      this.message = message;

+      // most problems don't have sub-messages, so init at zero size

+      childMessages = new ArrayList<String>(children.length);

+      for (int i = 0; i < children.length; i++) {

+        childMessages.add(children[i]);

+      }

+    }

+

+    public void addChild(String message) {

+      childMessages.add(message);

+    }

+

+    public String getPrimaryMessage() {

+      return message;

+    }

+

+    public Iterable<String> getSubMessages() {

+      return childMessages;

+    }

+

+    public boolean hasSubMessages() {

+      return !childMessages.isEmpty();

+    }

+  }

+

+  private Map<JClassType, List<Problem>> allProblems;

+  private Map<JClassType, List<Problem>> auxiliaries;

+  private Map<JClassType, List<Problem>> fatalProblems;

   private JClassType contextType;

 

   /**

@@ -61,9 +95,9 @@
               o2.getParameterizedQualifiedSourceName());

         }

       };

-    allProblems = new TreeMap<JClassType, List<String>>(comparator);

-    auxiliaries = new TreeMap<JClassType, List<String>>(comparator);

-    fatalProblems = new TreeMap<JClassType, List<String>>(comparator);

+    allProblems = new TreeMap<JClassType, List<Problem>>(comparator);

+    auxiliaries = new TreeMap<JClassType, List<Problem>>(comparator);

+    fatalProblems = new TreeMap<JClassType, List<Problem>>(comparator);

     contextType = null;

   }

 

@@ -77,7 +111,7 @@
    * @param extraLines additional continuation lines for the message, usually

    *    for additional explanations.

    */

-  public void add(JClassType type, String message, Priority priority, 

+  public Problem add(JClassType type, String message, Priority priority, 

       String... extraLines) {

     String contextString = "";

     if (contextType != null) {

@@ -85,62 +119,34 @@
           contextType.getParameterizedQualifiedSourceName() + ")";

     }

     message = message + contextString;

-    for (String line : extraLines) {

-      message = message + "\n   " + line;

-    }

+    Problem entry = new Problem(message, extraLines);

     if (priority == Priority.AUXILIARY) {

-      addToMap(type, message, auxiliaries);

-      return;

+      addToMap(type, entry, auxiliaries);

+      return entry;

     }

 

     // both FATAL and DEFAULT problems go in allProblems...

-    addToMap(type, message, allProblems);

+    addToMap(type, entry, allProblems);

 

     // only FATAL problems go in fatalProblems...

     if (priority == Priority.FATAL) {

-      addToMap(type, message, fatalProblems);

+      addToMap(type, entry, fatalProblems);

     }

+    return entry;

   }

 

-  /**

-   * Returns list of auxiliary "problems" logged against a given type.

-   *

-   * @param type type to fetch problems for

-   * @return {@code null} if no auxiliaries were logged.  Otherwise, a list

-   *   of strings describing messages, including the context in which the

-   *   problem was found.

-   */

-  public List<String> getAuxiliaryMessagesForType(JClassType type) {

-    List<String> list = auxiliaries.get(type);

+  public String getWorstMessageForType(JClassType type) {

+    List<Problem> list = fatalProblems.get(type);

     if (list == null) {

-      list = new ArrayList<String>(0);

+      list = allProblems.get(type);

+      if (list == null) {

+        list = auxiliaries.get(type);

+      }

     }

-    return list;

-  }

-

-  /**

-   * Returns list of problems logged against a given type.

-   *

-   * @param type type to fetch problems for

-   * @return {@code null} if no problems were logged.  Otherwise, a list

-   *   of strings describing problems, including the context in which the

-   *   problem was found.

-   */

-  public List<String> getProblemsForType(JClassType type) {

-    List<String> list = allProblems.get(type);

     if (list == null) {

-      list = new ArrayList<String>(0);

+      return null;

     }

-    return list;

-  }

-

-  /**

-   * Returns the number of types against which problems were reported.

-   *

-   * @return number of problematic types

-   */

-  public int getProblemTypeCount() {

-    return allProblems.size();

+    return list.get(0).getPrimaryMessage() + (list.size() > 1 ? ", etc." : "");

   }

 

   /**

@@ -189,20 +195,53 @@
   }

 

   /**

+   * Test accessor returning list of auxiliary "problems" logged against a

+   * given type.

+   *

+   * @param type type to fetch problems for

+   * @return {@code null} if no auxiliaries were logged.  Otherwise, a list

+   *   of strings describing messages, including the context in which the

+   *   problem was found.

+   */

+  List<Problem> getAuxiliaryMessagesForType(JClassType type) {

+    List<Problem> list = auxiliaries.get(type);

+    if (list == null) {

+      list = new ArrayList<Problem>(0);

+    }

+    return list;

+  }

+

+  /**

+   * Test accessor returning list of problems logged against a given type.

+   *

+   * @param type type to fetch problems for

+   * @return {@code null} if no problems were logged.  Otherwise, a list

+   *   of strings describing problems, including the context in which the

+   *   problem was found.

+   */

+  List<Problem> getProblemsForType(JClassType type) {

+    List<Problem> list = allProblems.get(type);

+    if (list == null) {

+      list = new ArrayList<Problem>(0);

+    }

+    return list;

+  }

+

+  /**

    * Adds an entry to one of the problem maps.

    *

    * @param type the type to add

    * @param message the message to add for {@code type}

    * @param map the map to add to

    */

-  private void addToMap(JClassType type, String message,

-      Map<JClassType, List<String>> map) {

-    List<String> entry = map.get(type);

-    if (entry == null) {

-      entry = new ArrayList<String>();

-      map.put(type, entry);

+  private void addToMap(JClassType type, Problem problem,

+      Map<JClassType, List<Problem>> map) {

+    List<Problem> list = map.get(type);

+    if (list == null) {

+      list = new ArrayList<Problem>();

+      map.put(type, list);

     }

-    entry.add(message);

+    list.add(problem);

   }

 

   /**

@@ -213,10 +252,17 @@
    * @param problems the problems to log

    */

   private void doReport(TreeLogger logger, Type level,

-      Map<JClassType, List<String>> problems) {

+      Map<JClassType, List<Problem>> problems) {

     for (JClassType type : problems.keySet()) {

-      for (String message : problems.get(type)) {

-        logger.log(level, message);

+      for (Problem problem : problems.get(type)) {

+        if (problem.hasSubMessages()) {

+          TreeLogger sublogger = logger.branch(level, problem.getPrimaryMessage());

+          for (String sub : problem.getSubMessages()) {

+            sublogger.log(level, sub);

+          }

+        } else {

+          logger.log(level, problem.getPrimaryMessage());

+        }

       }

     }

   }

diff --git a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
index 616367a..58db2d6 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/SerializableTypeOracleBuilder.java
@@ -1324,26 +1324,15 @@
     if (possiblyInstantiableTypes.size() == 0) {
       String possibilities[] = new String[candidates.size()];
       for (int i = 0; i < possibilities.length; i++) {
-        JClassType subtype = candidates.get(i); 
-        List<String> auxiliaries = problems.getAuxiliaryMessagesForType(subtype);
-        List<String> errors = problems.getProblemsForType(subtype);
-        if (errors.isEmpty()) {
-          if (auxiliaries.isEmpty()) {
-            possibilities[i] = "   subtype " + 
-              subtype.getParameterizedQualifiedSourceName() +
-              " is not instantiable";
-          } else {
-            // message with have the form "FQCN some-problem-description"
-            possibilities[i] = "   subtype " + auxiliaries.get(0);
-            if (auxiliaries.size() > 1) {
-              possibilities[i] = possibilities[i] + ", etc.";
-            }
-          }
+        JClassType subtype = candidates.get(i);
+        String worstMessage = problems.getWorstMessageForType(subtype);
+        if (worstMessage == null) {
+          possibilities[i] = "   subtype " + 
+            subtype.getParameterizedQualifiedSourceName() +
+            " is not instantiable";
         } else {
-          possibilities[i] = "   subtype " + errors.get(0);
-          if (errors.size() > 1 || !auxiliaries.isEmpty()) {
-            possibilities[i] = possibilities[i] + ", etc.";
-          }
+          // message with have the form "FQCN some-problem-description"
+          possibilities[i] = "   subtype " + worstMessage;
         }
       }
       problems.add(baseType, baseType.getParameterizedQualifiedSourceName()