1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.reporter 18 19 import java.io.File 20 import java.io.PrintWriter 21 22 interface Reporter { 23 24 /** 25 * Report an issue with a specific file. 26 * 27 * Delegates to calling `report(id, null, message, Location.forFile(file)`. 28 * 29 * @param id the id of the issue. 30 * @param file the optional source file for which the issue is reported. 31 * @param message the message to report. 32 * @param maximumSeverity the maximum [Severity] that will be reported. An issue that is 33 * configured to have a higher [Severity] that this will use the [maximumSeverity] instead. 34 * @return true if the issue was reported false it is a known issue in a baseline file. 35 */ reportnull36 fun report( 37 id: Issues.Issue, 38 file: File?, 39 message: String, 40 maximumSeverity: Severity = Severity.UNLIMITED, 41 ): Boolean { 42 val location = FileLocation.forFile(file) 43 return report(id, null, message, location, maximumSeverity) 44 } 45 46 /** 47 * Report an issue. 48 * 49 * The issue is handled as follows: 50 * 1. If the item is suppressed (see [isSuppressed]) then it will only be reported to a file 51 * into which suppressed issues are reported and this will return `false`. 52 * 2. If possible the issue will be checked in a relevant baseline file to see if it is a known 53 * issue and if so it will simply be ignored. 54 * 3. Otherwise, it will be reported at the appropriate severity to the command output and if 55 * possible it will be recorded in a new baseline file that the developer can copy to silence 56 * the issue in the future. 57 * 58 * If no [location] or [reportable] is provided then no location is reported in the error 59 * message, and the baseline file is neither checked nor updated. 60 * 61 * If a [location] is provided but no [reportable] then it is used both to report the message 62 * and as the baseline key to check and update the baseline file. 63 * 64 * If an [reportable] is provided but no [location] then it is used both to report the message 65 * and as the baseline key to check and update the baseline file. 66 * 67 * If both an [reportable] and [location] are provided then the [reportable] is used as the 68 * baseline key to check and update the baseline file and the [location] is used to report the 69 * message. The reason for that is the [location] is assumed to be a more accurate indication of 70 * where the problem lies but the [reportable] is assumed to provide a more stable key to use in 71 * the baseline as it will not change simply by adding and removing lines in the containing 72 * file. 73 * 74 * @param id the id of the issue. 75 * @param reportable the optional object for which the issue is reported. 76 * @param message the message to report. 77 * @param location the optional location to specify. 78 * @param maximumSeverity the maximum [Severity] that will be reported. An issue that is 79 * configured to have a higher [Severity] that this will use the [maximumSeverity] instead. 80 * @return true if the issue was reported false it is a known issue in a baseline file. 81 */ reportnull82 fun report( 83 id: Issues.Issue, 84 reportable: Reportable?, 85 message: String, 86 location: FileLocation = FileLocation.UNKNOWN, 87 maximumSeverity: Severity = Severity.UNLIMITED, 88 ): Boolean 89 90 /** 91 * Check to see whether the issue is suppressed. 92 * 1. If the [Severity] of the [Issues.Issue] is [Severity.HIDDEN] then this returns `true`. 93 * 2. If the [reportable] is `null` then this returns `false`. 94 * 3. If the item has a suppression annotation that lists the name of the issue then this 95 * returns `true`. 96 * 4. Otherwise, this returns `false`. 97 */ 98 fun isSuppressed( 99 id: Issues.Issue, 100 reportable: Reportable? = null, 101 message: String? = null 102 ): Boolean 103 } 104 105 /** 106 * Basic implementation of a [Reporter] that performs no filtering and simply outputs the message to 107 * the supplied [PrintWriter]. 108 */ 109 class BasicReporter(private val stderr: PrintWriter) : Reporter { 110 override fun report( 111 id: Issues.Issue, 112 reportable: Reportable?, 113 message: String, 114 location: FileLocation, 115 maximumSeverity: Severity, 116 ): Boolean { 117 stderr.println( 118 buildString { 119 val usableLocation = reportable?.fileLocation ?: location 120 append(usableLocation.path) 121 if (usableLocation.line > 0) { 122 append(":") 123 append(usableLocation.line) 124 } 125 append(": ") 126 val severity = id.defaultLevel 127 append(severity) 128 append(": ") 129 append(message) 130 append(severity.messageSuffix) 131 append(" [") 132 append(id.name) 133 append("]") 134 } 135 ) 136 return true 137 } 138 139 override fun isSuppressed( 140 id: Issues.Issue, 141 reportable: Reportable?, 142 message: String? 143 ): Boolean = false 144 } 145