duke@1: /* duke@1: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.util; duke@1: duke@1: import java.util.HashSet; duke@1: import java.util.Set; duke@1: import javax.tools.JavaFileObject; duke@1: duke@1: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; duke@1: duke@1: duke@1: /** duke@1: * A handler to process mandatory warnings, setting up a deferred diagnostic duke@1: * to be printed at the end of the compilation if some warnings get suppressed duke@1: * because too many warnings have already been generated. duke@1: * duke@1: * Note that the SuppressWarnings annotation can be used to suppress warnings duke@1: * about conditions that would otherwise merit a warning. Such processing duke@1: * is done when the condition is detected, and in those cases, no call is duke@1: * made on any API to generate a warning at all. In consequence, this handler only duke@1: * gets to handle those warnings that JLS says must be generated. duke@1: * duke@1: *

This is NOT part of any API supported by Sun Microsystems. If duke@1: * you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: public class MandatoryWarningHandler { duke@1: duke@1: /** duke@1: * The kinds of different deferred diagnostics that might be generated duke@1: * if a mandatory warning is suppressed because too many warnings have duke@1: * already been output. duke@1: * duke@1: * The parameter is a fragment used to build an I18N message key for Log. duke@1: */ duke@1: private enum DeferredDiagnosticKind { duke@1: /** duke@1: * This kind is used when a single specific file is found to have warnings duke@1: * and no similar warnings have already been given. duke@1: * It generates a message like: duke@1: * FILE has ISSUES duke@1: */ duke@1: IN_FILE(".filename"), duke@1: /** duke@1: * This kind is used when a single specific file is found to have warnings duke@1: * and when similar warnings have already been reported for the file. duke@1: * It generates a message like: duke@1: * FILE has additional ISSUES duke@1: */ duke@1: ADDITIONAL_IN_FILE(".filename.additional"), duke@1: /** duke@1: * This kind is used when multiple files have been found to have warnings, duke@1: * and none of them have had any similar warnings. duke@1: * It generates a message like: duke@1: * Some files have ISSUES duke@1: */ duke@1: IN_FILES(".plural"), duke@1: /** duke@1: * This kind is used when multiple files have been found to have warnings, duke@1: * and some of them have had already had specific similar warnings. duke@1: * It generates a message like: duke@1: * Some files have additional ISSUES duke@1: */ duke@1: ADDITIONAL_IN_FILES(".plural.additional"); duke@1: duke@1: DeferredDiagnosticKind(String v) { value = v; } duke@1: String getKey(String prefix) { return prefix + value; } duke@1: duke@1: private String value; duke@1: } duke@1: duke@1: duke@1: /** duke@1: * Create a handler for mandatory warnings. duke@1: * @param log The log on which to generate any diagnostics duke@1: * @param verbose Specify whether or not detailed messages about duke@1: * individual instances should be given, or whether an aggregate duke@1: * message should be generated at the end of the compilation. duke@1: * Typically set via -Xlint:option. jjg@60: * @param enforceMandatory jjg@60: * True if mandatory warnings and notes are being enforced. duke@1: * @param prefix A common prefix for the set of message keys for duke@1: * the messages that may be generated. duke@1: */ jjg@60: public MandatoryWarningHandler(Log log, boolean verbose, jjg@60: boolean enforceMandatory, String prefix) { duke@1: this.log = log; duke@1: this.verbose = verbose; duke@1: this.prefix = prefix; jjg@60: this.enforceMandatory = enforceMandatory; duke@1: } duke@1: duke@1: /** duke@1: * Report a mandatory warning. duke@1: */ duke@1: public void report(DiagnosticPosition pos, String msg, Object... args) { mcimadamore@168: JavaFileObject currentSource = log.currentSourceFile(); duke@1: duke@1: if (verbose) { duke@1: if (sourcesWithReportedWarnings == null) duke@1: sourcesWithReportedWarnings = new HashSet(); duke@1: duke@1: if (log.nwarnings < log.MaxWarnings) { duke@1: // generate message and remember the source file jjg@60: logMandatoryWarning(pos, msg, args); duke@1: sourcesWithReportedWarnings.add(currentSource); duke@1: } else if (deferredDiagnosticKind == null) { duke@1: // set up deferred message duke@1: if (sourcesWithReportedWarnings.contains(currentSource)) { duke@1: // more errors in a file that already has reported warnings duke@1: deferredDiagnosticKind = DeferredDiagnosticKind.ADDITIONAL_IN_FILE; duke@1: } else { duke@1: // warnings in a new source file duke@1: deferredDiagnosticKind = DeferredDiagnosticKind.IN_FILE; duke@1: } duke@1: deferredDiagnosticSource = currentSource; duke@1: deferredDiagnosticArg = currentSource; duke@1: } else if ((deferredDiagnosticKind == DeferredDiagnosticKind.IN_FILE duke@1: || deferredDiagnosticKind == DeferredDiagnosticKind.ADDITIONAL_IN_FILE) duke@1: && !equal(deferredDiagnosticSource, currentSource)) { duke@1: // additional errors in more than one source file duke@1: deferredDiagnosticKind = DeferredDiagnosticKind.ADDITIONAL_IN_FILES; duke@1: deferredDiagnosticArg = null; duke@1: } duke@1: } else { duke@1: if (deferredDiagnosticKind == null) { duke@1: // warnings in a single source duke@1: deferredDiagnosticKind = DeferredDiagnosticKind.IN_FILE; duke@1: deferredDiagnosticSource = currentSource; duke@1: deferredDiagnosticArg = currentSource; duke@1: } else if (deferredDiagnosticKind == DeferredDiagnosticKind.IN_FILE && duke@1: !equal(deferredDiagnosticSource, currentSource)) { duke@1: // warnings in multiple source files duke@1: deferredDiagnosticKind = DeferredDiagnosticKind.IN_FILES; duke@1: deferredDiagnosticArg = null; duke@1: } duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Report any diagnostic that might have been deferred by previous calls of report(). duke@1: */ duke@1: public void reportDeferredDiagnostic() { duke@1: if (deferredDiagnosticKind != null) { duke@1: if (deferredDiagnosticArg == null) jjg@60: logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix)); duke@1: else jjg@60: logMandatoryNote(deferredDiagnosticSource, deferredDiagnosticKind.getKey(prefix), deferredDiagnosticArg); duke@1: duke@1: if (!verbose) jjg@60: logMandatoryNote(deferredDiagnosticSource, prefix + ".recompile"); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Check two objects, each possibly null, are either both null or are equal. duke@1: */ duke@1: private static boolean equal(Object o1, Object o2) { duke@1: return ((o1 == null || o2 == null) ? (o1 == o2) : o1.equals(o2)); duke@1: } duke@1: duke@1: /** duke@1: * The log to which to report warnings. duke@1: */ duke@1: private Log log; duke@1: duke@1: /** duke@1: * Whether or not to report individual warnings, or simply to report a duke@1: * single aggregate warning at the end of the compilation. duke@1: */ duke@1: private boolean verbose; duke@1: duke@1: /** duke@1: * The common prefix for all I18N message keys generated by this handler. duke@1: */ duke@1: private String prefix; duke@1: duke@1: /** duke@1: * A set containing the names of the source files for which specific duke@1: * warnings have been generated -- i.e. in verbose mode. If a source name duke@1: * appears in this list, then deferred diagnostics will be phrased to duke@1: * include "additionally"... duke@1: */ duke@1: private Set sourcesWithReportedWarnings; duke@1: duke@1: /** duke@1: * A variable indicating the latest best guess at what the final duke@1: * deferred diagnostic will be. Initially as specific and helpful duke@1: * as possible, as more warnings are reported, the scope of the duke@1: * diagnostic will be broadened. duke@1: */ duke@1: private DeferredDiagnosticKind deferredDiagnosticKind; duke@1: duke@1: /** duke@1: * If deferredDiagnosticKind is IN_FILE or ADDITIONAL_IN_FILE, this variable duke@1: * gives the value of log.currentSource() for the file in question. duke@1: */ duke@1: private JavaFileObject deferredDiagnosticSource; duke@1: duke@1: /** duke@1: * An optional argument to be used when constructing the duke@1: * deferred diagnostic message, based on deferredDiagnosticKind. duke@1: * This variable should normally be set/updated whenever duke@1: * deferredDiagnosticKind is updated. duke@1: */ duke@1: private Object deferredDiagnosticArg; jjg@60: jjg@60: /** jjg@60: * True if mandatory warnings and notes are being enforced. jjg@60: */ jjg@60: private final boolean enforceMandatory; jjg@60: jjg@60: /** jjg@60: * Reports a mandatory warning to the log. If mandatory warnings jjg@60: * are not being enforced, treat this as an ordinary warning. jjg@60: */ jjg@60: private void logMandatoryWarning(DiagnosticPosition pos, String msg, jjg@60: Object... args) { jjg@60: if (enforceMandatory) jjg@60: log.mandatoryWarning(pos, msg, args); jjg@60: else jjg@60: log.warning(pos, msg, args); jjg@60: } jjg@60: jjg@60: /** jjg@60: * Reports a mandatory note to the log. If mandatory notes are jjg@60: * not being enforced, treat this as an ordinary note. jjg@60: */ jjg@60: private void logMandatoryNote(JavaFileObject file, String msg, Object... args) { jjg@60: if (enforceMandatory) jjg@60: log.mandatoryNote(file, msg, args); jjg@60: else jjg@60: log.note(file, msg, args); jjg@60: } duke@1: }