Home | History | Annotate | Download | only in gle2
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
      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.ide.eclipse.adt.internal.editors.layout.gle2;
     18 
     19 import com.android.annotations.NonNull;
     20 import com.android.annotations.Nullable;
     21 import com.android.ide.common.rendering.RenderSecurityManager;
     22 import com.android.ide.common.rendering.api.LayoutLog;
     23 import com.android.ide.eclipse.adt.AdtPlugin;
     24 
     25 import org.eclipse.core.runtime.IStatus;
     26 
     27 import java.util.ArrayList;
     28 import java.util.HashSet;
     29 import java.util.List;
     30 import java.util.Set;
     31 
     32 /**
     33  * A {@link LayoutLog} which records the problems it encounters and offers them as a
     34  * single summary at the end
     35  */
     36 public class RenderLogger extends LayoutLog {
     37     static final String TAG_MISSING_DIMENSION = "missing.dimension";     //$NON-NLS-1$
     38 
     39     private final String mName;
     40     private List<String> mFidelityWarnings;
     41     private List<String> mWarnings;
     42     private List<String> mErrors;
     43     private boolean mHaveExceptions;
     44     private List<String> mTags;
     45     private List<Throwable> mTraces;
     46     private static Set<String> sIgnoredFidelityWarnings;
     47     private final Object mCredential;
     48 
     49     /** Construct a logger for the given named layout */
     50     RenderLogger(String name, Object credential) {
     51         mName = name;
     52         mCredential = credential;
     53     }
     54 
     55     /**
     56      * Are there any logged errors or warnings during the render?
     57      *
     58      * @return true if there were problems during the render
     59      */
     60     public boolean hasProblems() {
     61         return mFidelityWarnings != null || mErrors != null || mWarnings != null ||
     62             mHaveExceptions;
     63     }
     64 
     65     /**
     66      * Returns a list of traces encountered during rendering, or null if none
     67      *
     68      * @return a list of traces encountered during rendering, or null if none
     69      */
     70     @Nullable
     71     public List<Throwable> getFirstTrace() {
     72         return mTraces;
     73     }
     74 
     75     /**
     76      * Returns a (possibly multi-line) description of all the problems
     77      *
     78      * @param includeFidelityWarnings if true, include fidelity warnings in the problem
     79      *            summary
     80      * @return a string describing the rendering problems
     81      */
     82     @NonNull
     83     public String getProblems(boolean includeFidelityWarnings) {
     84         StringBuilder sb = new StringBuilder();
     85 
     86         if (mErrors != null) {
     87             for (String error : mErrors) {
     88                 sb.append(error).append('\n');
     89             }
     90         }
     91 
     92         if (mWarnings != null) {
     93             for (String warning : mWarnings) {
     94                 sb.append(warning).append('\n');
     95             }
     96         }
     97 
     98         if (includeFidelityWarnings && mFidelityWarnings != null) {
     99             sb.append("The graphics preview in the layout editor may not be accurate:\n");
    100             for (String warning : mFidelityWarnings) {
    101                 sb.append("* ");
    102                 sb.append(warning).append('\n');
    103             }
    104         }
    105 
    106         if (mHaveExceptions) {
    107             sb.append("Exception details are logged in Window > Show View > Error Log");
    108         }
    109 
    110         return sb.toString();
    111     }
    112 
    113     /**
    114      * Returns the fidelity warnings
    115      *
    116      * @return the fidelity warnings
    117      */
    118     @Nullable
    119     public List<String> getFidelityWarnings() {
    120         return mFidelityWarnings;
    121     }
    122 
    123     // ---- extends LayoutLog ----
    124 
    125     @Override
    126     public void error(String tag, String message, Object data) {
    127         String description = describe(message);
    128 
    129         appendToIdeLog(null, IStatus.ERROR, description);
    130 
    131         // Workaround: older layout libraries don't provide a tag for this error
    132         if (tag == null && message != null
    133                 && message.startsWith("Failed to find style ")) { //$NON-NLS-1$
    134             tag = LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR;
    135         }
    136 
    137         addError(tag, description);
    138     }
    139 
    140     @Override
    141     public void error(String tag, String message, Throwable throwable, Object data) {
    142         String description = describe(message);
    143         appendToIdeLog(throwable, IStatus.ERROR, description);
    144 
    145         if (throwable != null) {
    146             if (throwable instanceof ClassNotFoundException) {
    147                 // The project callback is given a chance to resolve classes,
    148                 // and when it fails, it will record it in its own list which
    149                 // is displayed in a special way (with action hyperlinks etc).
    150                 // Therefore, include these messages in the visible render log,
    151                 // especially since the user message from a ClassNotFoundException
    152                 // is really not helpful (it just lists the class name without
    153                 // even mentioning that it is a class-not-found exception.)
    154                 return;
    155             }
    156 
    157             if (description.equals(throwable.getLocalizedMessage()) ||
    158                     description.equals(throwable.getMessage())) {
    159                 description = "Exception raised during rendering: " + description;
    160             }
    161             recordThrowable(throwable);
    162             mHaveExceptions = true;
    163         }
    164 
    165         addError(tag, description);
    166     }
    167 
    168     /**
    169      * Record that the given exception was encountered during rendering
    170      *
    171      * @param throwable the exception that was raised
    172      */
    173     public void recordThrowable(@NonNull Throwable throwable) {
    174         if (mTraces == null) {
    175             mTraces = new ArrayList<Throwable>();
    176         }
    177         mTraces.add(throwable);
    178     }
    179 
    180     @Override
    181     public void warning(String tag, String message, Object data) {
    182         String description = describe(message);
    183 
    184         boolean log = true;
    185         if (TAG_RESOURCES_FORMAT.equals(tag)) {
    186             if (description.equals("You must supply a layout_width attribute.")       //$NON-NLS-1$
    187                 || description.equals("You must supply a layout_height attribute.")) {//$NON-NLS-1$
    188                 tag = TAG_MISSING_DIMENSION;
    189                 log = false;
    190             }
    191         }
    192 
    193         if (log) {
    194             appendToIdeLog(null, IStatus.WARNING, description);
    195         }
    196 
    197         addWarning(tag, description);
    198     }
    199 
    200     @Override
    201     public void fidelityWarning(String tag, String message, Throwable throwable, Object data) {
    202         if (sIgnoredFidelityWarnings != null && sIgnoredFidelityWarnings.contains(message)) {
    203             return;
    204         }
    205 
    206         String description = describe(message);
    207         appendToIdeLog(throwable, IStatus.ERROR, description);
    208 
    209         if (throwable != null) {
    210             mHaveExceptions = true;
    211         }
    212 
    213         addFidelityWarning(tag, description);
    214     }
    215 
    216     /**
    217      * Ignore the given render fidelity warning for the current session
    218      *
    219      * @param message the message to be ignored for this session
    220      */
    221     public static void ignoreFidelityWarning(String message) {
    222         if (sIgnoredFidelityWarnings == null) {
    223             sIgnoredFidelityWarnings = new HashSet<String>();
    224         }
    225         sIgnoredFidelityWarnings.add(message);
    226     }
    227 
    228     @NonNull
    229     private String describe(@Nullable String message) {
    230         if (message == null) {
    231             return "";
    232         } else {
    233             return message;
    234         }
    235     }
    236 
    237     private void addWarning(String tag, String description) {
    238         if (mWarnings == null) {
    239             mWarnings = new ArrayList<String>();
    240         } else if (mWarnings.contains(description)) {
    241             // Avoid duplicates
    242             return;
    243         }
    244         mWarnings.add(description);
    245         addTag(tag);
    246     }
    247 
    248     private void addError(String tag, String description) {
    249         if (mErrors == null) {
    250             mErrors = new ArrayList<String>();
    251         } else if (mErrors.contains(description)) {
    252             // Avoid duplicates
    253             return;
    254         }
    255         mErrors.add(description);
    256         addTag(tag);
    257     }
    258 
    259     private void addFidelityWarning(String tag, String description) {
    260         if (mFidelityWarnings == null) {
    261             mFidelityWarnings = new ArrayList<String>();
    262         } else if (mFidelityWarnings.contains(description)) {
    263             // Avoid duplicates
    264             return;
    265         }
    266         mFidelityWarnings.add(description);
    267         addTag(tag);
    268     }
    269 
    270     // ---- Tags ----
    271 
    272     private void addTag(String tag) {
    273         if (tag != null) {
    274             if (mTags == null) {
    275                 mTags = new ArrayList<String>();
    276             }
    277             mTags.add(tag);
    278         }
    279     }
    280 
    281     /**
    282      * Returns true if the given tag prefix has been seen
    283      *
    284      * @param prefix the tag prefix to look for
    285      * @return true iff any tags with the given prefix was seen during the render
    286      */
    287     public boolean seenTagPrefix(String prefix) {
    288         if (mTags != null) {
    289             for (String tag : mTags) {
    290                 if (tag.startsWith(prefix)) {
    291                     return true;
    292                 }
    293             }
    294         }
    295 
    296         return false;
    297     }
    298 
    299     /**
    300      * Returns true if the given tag has been seen
    301      *
    302      * @param tag the tag to look for
    303      * @return true iff the tag was seen during the render
    304      */
    305     public boolean seenTag(String tag) {
    306         if (mTags != null) {
    307             return mTags.contains(tag);
    308         } else {
    309             return false;
    310         }
    311     }
    312 
    313     // Append the given message to the ADT log. Bypass the sandbox if necessary
    314     // such that we can write to the log file.
    315     private void appendToIdeLog(Throwable throwable, int severity, String description) {
    316         boolean token = RenderSecurityManager.enterSafeRegion(mCredential);
    317         try {
    318             if (throwable != null) {
    319                 AdtPlugin.log(throwable, "%1$s: %2$s", mName, description);
    320             } else {
    321                 AdtPlugin.log(severity, "%1$s: %2$s", mName, description);
    322             }
    323         } finally {
    324             RenderSecurityManager.exitSafeRegion(token);
    325         }
    326     }
    327 }
    328