/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php * * 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.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.eclipse.adt.AdtPlugin; import org.eclipse.core.runtime.IStatus; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * A {@link LayoutLog} which records the problems it encounters and offers them as a * single summary at the end */ public class RenderLogger extends LayoutLog { static final String TAG_MISSING_DIMENSION = "missing.dimension"; //$NON-NLS-1$ private final String mName; private List<String> mFidelityWarnings; private List<String> mWarnings; private List<String> mErrors; private boolean mHaveExceptions; private List<String> mTags; private List<Throwable> mTraces; private static Set<String> sIgnoredFidelityWarnings; /** Construct a logger for the given named layout */ RenderLogger(String name) { mName = name; } /** * Are there any logged errors or warnings during the render? * * @return true if there were problems during the render */ public boolean hasProblems() { return mFidelityWarnings != null || mErrors != null || mWarnings != null || mHaveExceptions; } /** * Returns a list of traces encountered during rendering, or null if none * * @return a list of traces encountered during rendering, or null if none */ @Nullable public List<Throwable> getFirstTrace() { return mTraces; } /** * Returns a (possibly multi-line) description of all the problems * * @param includeFidelityWarnings if true, include fidelity warnings in the problem * summary * @return a string describing the rendering problems */ @NonNull public String getProblems(boolean includeFidelityWarnings) { StringBuilder sb = new StringBuilder(); if (mErrors != null) { for (String error : mErrors) { sb.append(error).append('\n'); } } if (mWarnings != null) { for (String warning : mWarnings) { sb.append(warning).append('\n'); } } if (includeFidelityWarnings && mFidelityWarnings != null) { sb.append("The graphics preview in the layout editor may not be accurate:\n"); for (String warning : mFidelityWarnings) { sb.append("* "); sb.append(warning).append('\n'); } } if (mHaveExceptions) { sb.append("Exception details are logged in Window > Show View > Error Log"); } return sb.toString(); } /** * Returns the fidelity warnings * * @return the fidelity warnings */ @Nullable public List<String> getFidelityWarnings() { return mFidelityWarnings; } // ---- extends LayoutLog ---- @Override public void error(String tag, String message, Object data) { String description = describe(message); AdtPlugin.log(IStatus.ERROR, "%1$s: %2$s", mName, description); // Workaround: older layout libraries don't provide a tag for this error if (tag == null && message != null && message.startsWith("Failed to find style ")) { //$NON-NLS-1$ tag = LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR; } addError(tag, description); } @Override public void error(String tag, String message, Throwable throwable, Object data) { String description = describe(message); AdtPlugin.log(throwable, "%1$s: %2$s", mName, description); if (throwable != null) { if (throwable instanceof ClassNotFoundException) { // The project callback is given a chance to resolve classes, // and when it fails, it will record it in its own list which // is displayed in a special way (with action hyperlinks etc). // Therefore, include these messages in the visible render log, // especially since the user message from a ClassNotFoundException // is really not helpful (it just lists the class name without // even mentioning that it is a class-not-found exception.) return; } if (description.equals(throwable.getLocalizedMessage()) || description.equals(throwable.getMessage())) { description = "Exception raised during rendering: " + description; } recordThrowable(throwable); mHaveExceptions = true; } addError(tag, description); } /** * Record that the given exception was encountered during rendering * * @param throwable the exception that was raised */ public void recordThrowable(@NonNull Throwable throwable) { if (mTraces == null) { mTraces = new ArrayList<Throwable>(); } mTraces.add(throwable); } @Override public void warning(String tag, String message, Object data) { String description = describe(message); boolean log = true; if (TAG_RESOURCES_FORMAT.equals(tag)) { if (description.equals("You must supply a layout_width attribute.") //$NON-NLS-1$ || description.equals("You must supply a layout_height attribute.")) {//$NON-NLS-1$ tag = TAG_MISSING_DIMENSION; log = false; } } if (log) { AdtPlugin.log(IStatus.WARNING, "%1$s: %2$s", mName, description); } addWarning(tag, description); } @Override public void fidelityWarning(String tag, String message, Throwable throwable, Object data) { if (sIgnoredFidelityWarnings != null && sIgnoredFidelityWarnings.contains(message)) { return; } String description = describe(message); AdtPlugin.log(throwable, "%1$s: %2$s", mName, description); if (throwable != null) { mHaveExceptions = true; } addFidelityWarning(tag, description); } /** * Ignore the given render fidelity warning for the current session * * @param message the message to be ignored for this session */ public static void ignoreFidelityWarning(String message) { if (sIgnoredFidelityWarnings == null) { sIgnoredFidelityWarnings = new HashSet<String>(); } sIgnoredFidelityWarnings.add(message); } @NonNull private String describe(@Nullable String message) { if (message == null) { return ""; } else { return message; } } private void addWarning(String tag, String description) { if (mWarnings == null) { mWarnings = new ArrayList<String>(); } else if (mWarnings.contains(description)) { // Avoid duplicates return; } mWarnings.add(description); addTag(tag); } private void addError(String tag, String description) { if (mErrors == null) { mErrors = new ArrayList<String>(); } else if (mErrors.contains(description)) { // Avoid duplicates return; } mErrors.add(description); addTag(tag); } private void addFidelityWarning(String tag, String description) { if (mFidelityWarnings == null) { mFidelityWarnings = new ArrayList<String>(); } else if (mFidelityWarnings.contains(description)) { // Avoid duplicates return; } mFidelityWarnings.add(description); addTag(tag); } // ---- Tags ---- private void addTag(String tag) { if (tag != null) { if (mTags == null) { mTags = new ArrayList<String>(); } mTags.add(tag); } } /** * Returns true if the given tag prefix has been seen * * @param prefix the tag prefix to look for * @return true iff any tags with the given prefix was seen during the render */ public boolean seenTagPrefix(String prefix) { if (mTags != null) { for (String tag : mTags) { if (tag.startsWith(prefix)) { return true; } } } return false; } /** * Returns true if the given tag has been seen * * @param tag the tag to look for * @return true iff the tag was seen during the render */ public boolean seenTag(String tag) { if (mTags != null) { return mTags.contains(tag); } else { return false; } } }