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