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