| /* |
| * Copyright 2008 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.gwt.user.rebind.rpc; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.core.ext.TreeLogger.Type; |
| import com.google.gwt.core.ext.typeinfo.JClassType; |
| |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| /** |
| * A collection of reported problems; these are accumulated during the |
| * SerializableTypeOracleBuilder's isSerializable analysis, and what to do about |
| * the problems is decided only later. |
| */ |
| public class ProblemReport { |
| |
| /** |
| * Priority of problems. {@link #FATAL} problems will fail a build that would |
| * otherwise have succeeded, for example because of a bad custom serializer |
| * used only as a subclass of a superclass with other viable subtypes. |
| * {@link #DEFAULT} problems might or might not be fatal, depending on overall |
| * results accumulated later. {@link #AUXILIARY} problems are not fatal, and |
| * often not even problems by themselves, but diagnostics related to default |
| * problems (e.g. type filtration, which might suppress an |
| * intended-to-serialize class). |
| */ |
| public enum Priority { |
| FATAL, DEFAULT, AUXILIARY |
| } |
| |
| /** |
| * An individual report, which may require multiple entries (expressed as logs |
| * under a branchpoint), but relates to an individual issue. |
| */ |
| public static 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; |
| |
| /** |
| * Creates a new, empty, context-less ProblemReport. |
| */ |
| public ProblemReport() { |
| Comparator<JClassType> comparator = new Comparator<JClassType>() { |
| public int compare(JClassType o1, JClassType o2) { |
| assert o1 != null; |
| assert o2 != null; |
| return o1.getParameterizedQualifiedSourceName().compareTo( |
| o2.getParameterizedQualifiedSourceName()); |
| } |
| }; |
| allProblems = new TreeMap<JClassType, List<Problem>>(comparator); |
| auxiliaries = new TreeMap<JClassType, List<Problem>>(comparator); |
| fatalProblems = new TreeMap<JClassType, List<Problem>>(comparator); |
| contextType = null; |
| } |
| |
| /** |
| * Adds a problem for a given type. This also sorts the problems into |
| * collections by priority. |
| * |
| * @param type the problematic type |
| * @param message the description of the problem |
| * @param priority priority of the problem. |
| * @param extraLines additional continuation lines for the message, usually |
| * for additional explanations. |
| */ |
| public Problem add(JClassType type, String message, Priority priority, String... extraLines) { |
| String contextString = ""; |
| if (contextType != null) { |
| contextString = " (reached via " + contextType.getParameterizedQualifiedSourceName() + ")"; |
| } |
| message = message + contextString; |
| Problem entry = new Problem(message, extraLines); |
| if (priority == Priority.AUXILIARY) { |
| addToMap(type, entry, auxiliaries); |
| return entry; |
| } |
| |
| // both FATAL and DEFAULT problems go in allProblems... |
| addToMap(type, entry, allProblems); |
| |
| // only FATAL problems go in fatalProblems... |
| if (priority == Priority.FATAL) { |
| addToMap(type, entry, fatalProblems); |
| } |
| return entry; |
| } |
| |
| public String getWorstMessageForType(JClassType type) { |
| List<Problem> list = fatalProblems.get(type); |
| if (list == null) { |
| list = allProblems.get(type); |
| if (list == null) { |
| list = auxiliaries.get(type); |
| } |
| } |
| if (list == null) { |
| return null; |
| } |
| return list.get(0).getPrimaryMessage() + (list.size() > 1 ? ", etc." : ""); |
| } |
| |
| /** |
| * Were any problems reported as "fatal"? |
| */ |
| public boolean hasFatalProblems() { |
| return !fatalProblems.isEmpty(); |
| } |
| |
| /** |
| * Reports all problems to the logger supplied, at the log level supplied. The |
| * problems are assured of being reported in lexographic order of type names. |
| * |
| * @param logger logger to receive problem reports |
| * @param problemLevel severity level at which to report problems. |
| * @param auxLevel severity level at which to report any auxiliary messages. |
| */ |
| public void report(TreeLogger logger, TreeLogger.Type problemLevel, TreeLogger.Type auxLevel) { |
| doReport(logger, auxLevel, auxiliaries); |
| doReport(logger, problemLevel, allProblems); |
| } |
| |
| /** |
| * Reports only urgent problems to the logger supplied, at the log level |
| * supplied. The problems are assured of being reported in lexographic order |
| * of type names. |
| * |
| * @param logger logger to receive problem reports |
| * @param level severity level at which to report problems. |
| */ |
| public void reportFatalProblems(TreeLogger logger, TreeLogger.Type level) { |
| doReport(logger, level, fatalProblems); |
| } |
| |
| /** |
| * Sets the context type currently being analyzed. Problems found will include |
| * reference to this context, until reset with another call to this method. |
| * Context may be canceled with a {@code null} value here. |
| * |
| * @param newContext the type under analysis |
| */ |
| public void setContextType(JClassType newContext) { |
| contextType = newContext; |
| } |
| |
| /** |
| * 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, Problem problem, Map<JClassType, List<Problem>> map) { |
| List<Problem> list = map.get(type); |
| if (list == null) { |
| list = new ArrayList<Problem>(); |
| map.put(type, list); |
| } |
| list.add(problem); |
| } |
| |
| /** |
| * Logs all of the problems from one of the problem maps. |
| * |
| * @param logger the logger to log to |
| * @param level the level for messages |
| * @param problems the problems to log |
| */ |
| private void doReport(TreeLogger logger, Type level, Map<JClassType, List<Problem>> problems) { |
| if (!logger.isLoggable(level)) { |
| return; |
| } |
| for (List<Problem> problemList : problems.values()) { |
| for (Problem problem : problemList) { |
| 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()); |
| } |
| } |
| } |
| } |
| } |