|  | // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  | package com.android.tools.r8.utils; | 
|  |  | 
|  | import static com.android.tools.r8.DiagnosticsLevel.ERROR; | 
|  | import static com.android.tools.r8.DiagnosticsLevel.INFO; | 
|  | import static com.android.tools.r8.DiagnosticsLevel.WARNING; | 
|  |  | 
|  | import com.android.tools.r8.Diagnostic; | 
|  | import com.android.tools.r8.DiagnosticsHandler; | 
|  | import com.android.tools.r8.DiagnosticsLevel; | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  |  | 
|  | public class Reporter implements DiagnosticsHandler { | 
|  |  | 
|  | private static class DiagnosticsLevelMapping { | 
|  | private final DiagnosticsLevel from; | 
|  | private final DiagnosticsLevel to; | 
|  | private final String diagnosticsClassName; | 
|  |  | 
|  | public DiagnosticsLevelMapping( | 
|  | DiagnosticsLevel from, DiagnosticsLevel to, String diagnosticsClassName) { | 
|  | this.from = from; | 
|  | this.to = to; | 
|  | this.diagnosticsClassName = diagnosticsClassName; | 
|  | } | 
|  |  | 
|  | public DiagnosticsLevel map(DiagnosticsLevel level, Diagnostic diagnostic) { | 
|  | if (level != from) { | 
|  | return level; | 
|  | } | 
|  | if (diagnosticsClassName.length() == 0 | 
|  | || diagnosticsClassName.equals(diagnostic.getClass().getSimpleName()) | 
|  | || diagnosticsClassName.equals(diagnostic.getClass().getTypeName())) { | 
|  | return to; | 
|  | } | 
|  | // Some diagnostics are exposed as interfaces, and implemented by internal implementations. | 
|  | for (Class<?> clazz : diagnostic.getClass().getInterfaces()) { | 
|  | if (diagnosticsClassName.equals(clazz.getSimpleName()) | 
|  | || diagnosticsClassName.equals(clazz.getTypeName())) { | 
|  | return to; | 
|  | } | 
|  | } | 
|  | return level; | 
|  | } | 
|  | } | 
|  |  | 
|  | private final DiagnosticsHandler clientHandler; | 
|  | private final List<DiagnosticsLevelMapping> diagnosticsLevelMapping = new ArrayList<>(); | 
|  | private AbortException abort = null; | 
|  |  | 
|  | public Reporter() { | 
|  | this(new DiagnosticsHandler() {}); | 
|  | } | 
|  |  | 
|  | public Reporter(DiagnosticsHandler clientHandler) { | 
|  | this.clientHandler = clientHandler; | 
|  | } | 
|  |  | 
|  | private void handleDiagnostic(DiagnosticsLevel level, Diagnostic diagnostic) { | 
|  | // To avoid having an entry for fatal error in the public  API enum use null to signal | 
|  | // fatal error internally. | 
|  | if (level != null) { | 
|  | DiagnosticsLevel modifiedLevel = clientHandler.modifyDiagnosticsLevel(level, diagnostic); | 
|  | level = modifiedLevel != null ? modifiedLevel : level; | 
|  | level = mapDiagnosticsLevel(level, diagnostic); | 
|  | } else { | 
|  | level = ERROR; | 
|  | } | 
|  | switch (level) { | 
|  | case INFO: | 
|  | clientHandler.info(diagnostic); | 
|  | break; | 
|  | case WARNING: | 
|  | clientHandler.warning(diagnostic); | 
|  | break; | 
|  | case ERROR: | 
|  | abort = new AbortException(diagnostic); | 
|  | clientHandler.error(diagnostic); | 
|  | break; | 
|  | default: | 
|  | throw new Unreachable(); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public synchronized void info(Diagnostic info) { | 
|  | handleDiagnostic(INFO, info); | 
|  | } | 
|  |  | 
|  | public void info(String message) { | 
|  | info(new StringDiagnostic(message)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public synchronized void warning(Diagnostic warning) { | 
|  | handleDiagnostic(WARNING, warning); | 
|  | } | 
|  |  | 
|  | public void warning(String message) { | 
|  | warning(new StringDiagnostic(message)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public synchronized void error(Diagnostic error) { | 
|  | handleDiagnostic(ERROR, error); | 
|  | } | 
|  |  | 
|  | public void error(String message) { | 
|  | error(new StringDiagnostic(message)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @throws AbortException always. | 
|  | */ | 
|  | public RuntimeException fatalError(String message) { | 
|  | throw fatalError(new StringDiagnostic(message)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @throws AbortException always. | 
|  | */ | 
|  | public RuntimeException fatalError(Diagnostic error) { | 
|  | handleDiagnostic(null, error); | 
|  | throw abort; | 
|  | } | 
|  |  | 
|  | /** @throws AbortException if any error was reported. */ | 
|  | public synchronized void failIfPendingErrors() { | 
|  | if (abort != null) { | 
|  | throw new RuntimeException(abort); | 
|  | } | 
|  | } | 
|  |  | 
|  | private DiagnosticsLevel mapDiagnosticsLevel(DiagnosticsLevel level, Diagnostic diagnostic) { | 
|  | for (DiagnosticsLevelMapping diagnosticsLevelMapping : diagnosticsLevelMapping) { | 
|  | level = diagnosticsLevelMapping.map(level, diagnostic); | 
|  | } | 
|  | return level; | 
|  | } | 
|  |  | 
|  | public void addDiagnosticsLevelMapping( | 
|  | DiagnosticsLevel from, String diagnosticsClassName, DiagnosticsLevel to) { | 
|  | diagnosticsLevelMapping.add(new DiagnosticsLevelMapping(from, to, diagnosticsClassName)); | 
|  | } | 
|  | } |