blob: 8548830bd675d49ffd267c2e64d59e306fb7a438 [file] [log] [blame]
/*
* 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.RenderSecurityManager;
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;
private final Object mCredential;
/** Construct a logger for the given named layout */
RenderLogger(String name, Object credential) {
mName = name;
mCredential = credential;
}
/**
* 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);
appendToIdeLog(null, IStatus.ERROR, 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);
appendToIdeLog(throwable, IStatus.ERROR, 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) {
appendToIdeLog(null, IStatus.WARNING, 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);
appendToIdeLog(throwable, IStatus.ERROR, 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;
}
}
// Append the given message to the ADT log. Bypass the sandbox if necessary
// such that we can write to the log file.
private void appendToIdeLog(Throwable throwable, int severity, String description) {
boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
try {
if (throwable != null) {
AdtPlugin.log(throwable, "%1$s: %2$s", mName, description);
} else {
AdtPlugin.log(severity, "%1$s: %2$s", mName, description);
}
} finally {
RenderSecurityManager.exitSafeRegion(token);
}
}
}