1 /* 2 * Copyright (C) 2011 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 package com.android.ide.eclipse.adt.internal.lint; 17 18 import com.android.annotations.NonNull; 19 import com.android.annotations.Nullable; 20 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 21 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils; 22 import com.android.tools.lint.detector.api.Issue; 23 24 import org.eclipse.core.resources.IFile; 25 import org.eclipse.core.resources.IFolder; 26 import org.eclipse.core.resources.IMarker; 27 import org.eclipse.core.resources.IProject; 28 import org.eclipse.core.resources.IResource; 29 import org.eclipse.jface.resource.JFaceResources; 30 import org.eclipse.jface.viewers.StyledString; 31 import org.eclipse.swt.graphics.Font; 32 import org.eclipse.swt.graphics.Image; 33 import org.eclipse.ui.ISharedImages; 34 import org.eclipse.ui.PlatformUI; 35 36 import java.io.File; 37 import java.util.Comparator; 38 39 /** A column shown in the {@link LintList} */ 40 abstract class LintColumn implements Comparator<IMarker> { 41 protected final LintList mList; 42 LintColumn(@onNull LintList list)43 protected LintColumn(@NonNull LintList list) { 44 mList = list; 45 } 46 47 /** @return true if this column should be shown by default */ isVisibleByDefault()48 public boolean isVisibleByDefault() { 49 return true; 50 } 51 52 /** @return true if this column's text should be left aligned */ isLeftAligned()53 public boolean isLeftAligned() { 54 return true; 55 } 56 57 /** 58 * @return the number of pixels that this column should show by default 59 */ getPreferredWidth()60 public int getPreferredWidth() { 61 return getPreferredCharWidth() * SwtUtils.getAverageCharWidth(mList.getDisplay(), 62 mList.getTree().getFont()); 63 } 64 65 /** 66 * @return the number of characters that this column should show by default 67 */ getPreferredCharWidth()68 public int getPreferredCharWidth() { 69 return 15; 70 } 71 72 /** 73 * @return the title of the column 74 */ 75 @NonNull getColumnHeaderText()76 public abstract String getColumnHeaderText(); 77 78 /** 79 * @return the image of the column, or null 80 */ getColumnHeaderImage()81 public Image getColumnHeaderImage() { 82 return null; 83 } 84 85 /** 86 * @param marker the {@link IMarker} to get the value for 87 * @return the value of this column for the given marker 88 */ getValue(@onNull IMarker marker)89 public abstract String getValue(@NonNull IMarker marker); 90 91 /** 92 * @param marker the {@link IMarker} to get the value for 93 * @return the styled value of this column for the given marker 94 */ getStyledValue(@onNull IMarker marker)95 public StyledString getStyledValue(@NonNull IMarker marker) { 96 return null; 97 } 98 99 /** 100 * @param marker the {@link IMarker} to get the image for 101 * @return The image for this particular column, or null 102 */ 103 @Nullable getImage(@onNull IMarker marker)104 public Image getImage(@NonNull IMarker marker) { 105 return null; 106 } 107 108 /** 109 * @param marker the {@link IMarker} to get the font for 110 * @return The font for this particular column, or null 111 */ 112 @Nullable getFont(@onNull IMarker marker)113 public Font getFont(@NonNull IMarker marker) { 114 return null; 115 } 116 117 /** 118 * @return true if the sort should be in ascending order. If false, sort in descending order. 119 */ isAscending()120 public boolean isAscending() { 121 return true; 122 } 123 124 /** 125 * @return true if this column should be visible by default 126 */ visibleByDefault()127 public boolean visibleByDefault() { 128 return true; 129 } 130 131 @Override compare(IMarker o1, IMarker o2)132 public int compare(IMarker o1, IMarker o2) { 133 return getValue(o1).compareTo(getValue(o2)); 134 } 135 136 // Used for default LabelProvider 137 @Override toString()138 public String toString() { 139 return getColumnHeaderText(); 140 } 141 142 static class MessageColumn extends LintColumn { 143 MessageColumn(LintList list)144 public MessageColumn(LintList list) { 145 super(list); 146 } 147 148 @Override getColumnHeaderText()149 public @NonNull String getColumnHeaderText() { 150 return "Description"; 151 } 152 153 @Override getPreferredCharWidth()154 public int getPreferredCharWidth() { 155 return 80; 156 } 157 158 @Override getValue(@onNull IMarker marker)159 public String getValue(@NonNull IMarker marker) { 160 return getStyledValue(marker).toString(); 161 } 162 163 @Override getStyledValue(@onNull IMarker marker)164 public StyledString getStyledValue(@NonNull IMarker marker) { 165 StyledString styledString = new StyledString(); 166 167 String message = marker.getAttribute(IMarker.MESSAGE, ""); 168 styledString.append(message); 169 170 int count = mList.getCount(marker); 171 if (count > 1) { 172 styledString.append(String.format(" (%2$d items)", message, count), 173 StyledString.COUNTER_STYLER); 174 } 175 176 return styledString; 177 } 178 179 @Override getImage(@onNull IMarker marker)180 public Image getImage(@NonNull IMarker marker) { 181 int severity = marker.getAttribute(IMarker.SEVERITY, 0); 182 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); 183 switch (severity) { 184 case IMarker.SEVERITY_ERROR: 185 if (LintFix.hasFix(EclipseLintClient.getId(marker))) { 186 return IconFactory.getInstance().getIcon("quickfix_error"); //$NON-NLS-1$ 187 } 188 return sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK); 189 case IMarker.SEVERITY_WARNING: 190 if (LintFix.hasFix(EclipseLintClient.getId(marker))) { 191 return IconFactory.getInstance().getIcon("quickfix_warning"); //$NON-NLS-1$ 192 } 193 return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK); 194 case IMarker.SEVERITY_INFO: 195 return sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK); 196 default: 197 return null; 198 } 199 } 200 201 @Override getFont(@onNull IMarker marker)202 public Font getFont(@NonNull IMarker marker) { 203 int severity = marker.getAttribute(IMarker.SEVERITY, 0); 204 if (severity == IMarker.SEVERITY_ERROR) { 205 return JFaceResources.getFontRegistry().getBold( 206 JFaceResources.DEFAULT_FONT); 207 } 208 209 return null; 210 } 211 212 @Override isAscending()213 public boolean isAscending() { 214 return false; 215 } 216 217 @Override compare(IMarker marker2, IMarker marker1)218 public int compare(IMarker marker2, IMarker marker1) { 219 // Reversing order marker1/marker2 here since we want to use a "descending" column 220 // sorting marker to indicate priority. (Note that we return from isAscending too.) 221 222 String id1 = EclipseLintClient.getId(marker1); 223 String id2 = EclipseLintClient.getId(marker2); 224 if (id1 == null || id2 == null) { 225 return marker1.getResource().getName().compareTo( 226 marker2.getResource().getName()); 227 } 228 Issue issue1 = mList.getIssue(id1); 229 Issue issue2 = mList.getIssue(id2); 230 if (issue1 == null || issue2 == null) { 231 // Unknown issue? Can happen if you have used a third party detector 232 // which is no longer available but which left a persistent marker behind 233 return id1.compareTo(id2); 234 } 235 int delta = mList.getSeverity(issue1).ordinal() - 236 mList.getSeverity(issue2).ordinal(); 237 if (delta != 0) { 238 return delta; 239 } 240 delta = issue2.getPriority() - issue1.getPriority(); 241 if (delta != 0) { 242 return delta; 243 } 244 delta = issue1.getCategory().compareTo(issue2.getCategory()); 245 if (delta != 0) { 246 return delta; 247 } 248 delta = id1.compareTo(id2); 249 if (delta != 0) { 250 return delta; 251 } 252 253 IResource resource1 = marker1.getResource(); 254 IResource resource2 = marker2.getResource(); 255 256 IProject project1 = resource1.getProject(); 257 IProject project2 = resource2.getProject(); 258 delta = project1.getName().compareTo(project2.getName()); 259 if (delta != 0) { 260 return delta; 261 } 262 263 delta = resource1.getName().compareTo(resource2.getName()); 264 if (delta != 0) { 265 return delta; 266 } 267 268 return marker1.getAttribute(IMarker.LINE_NUMBER, 0) 269 - marker2.getAttribute(IMarker.LINE_NUMBER, 0); 270 } 271 } 272 273 static class CategoryColumn extends LintColumn { 274 CategoryColumn(LintList list)275 public CategoryColumn(LintList list) { 276 super(list); 277 } 278 279 @Override getColumnHeaderText()280 public @NonNull String getColumnHeaderText() { 281 return "Category"; 282 } 283 284 @Override getPreferredCharWidth()285 public int getPreferredCharWidth() { 286 return 20; 287 } 288 289 @Override getValue(@onNull IMarker marker)290 public String getValue(@NonNull IMarker marker) { 291 Issue issue = mList.getIssue(marker); 292 if (issue != null) { 293 return issue.getCategory().getFullName(); 294 } else { 295 return ""; 296 } 297 } 298 } 299 300 static class LocationColumn extends LintColumn { LocationColumn(LintList list)301 public LocationColumn(LintList list) { 302 super(list); 303 } 304 305 @Override getColumnHeaderText()306 public @NonNull String getColumnHeaderText() { 307 return "Location"; 308 } 309 310 @Override getPreferredCharWidth()311 public int getPreferredCharWidth() { 312 return 35; 313 } 314 315 @Override getValue(@onNull IMarker marker)316 public String getValue(@NonNull IMarker marker) { 317 return getStyledValue(marker).toString(); 318 } 319 320 @Override getStyledValue(@onNull IMarker marker)321 public StyledString getStyledValue(@NonNull IMarker marker) { 322 StyledString styledString = new StyledString(); 323 324 // Combined location 325 IResource resource = marker.getResource(); 326 if (resource instanceof IProject) { 327 styledString.append(resource.getName()); 328 } else { 329 // Show location as Parent/File:Line in Project 330 styledString.append(resource.getName()); 331 if (resource instanceof IFile) { 332 int line = marker.getAttribute(IMarker.LINE_NUMBER, -1); 333 if (line > 1) { 334 styledString.append(':').append(Integer.toString(line)); 335 } 336 } else if (resource instanceof IFolder) { 337 styledString.append(File.separatorChar); 338 } 339 340 if (!(resource.getParent() instanceof IProject)) { 341 styledString.append(" in "); 342 styledString.append(resource.getParent().getName(), 343 StyledString.DECORATIONS_STYLER); 344 } 345 346 styledString.append(String.format(" (%1$s)", resource.getProject().getName()), 347 StyledString.QUALIFIER_STYLER); 348 } 349 350 return styledString; 351 } 352 353 @Override compare(IMarker marker1, IMarker marker2)354 public int compare(IMarker marker1, IMarker marker2) { 355 IResource resource1 = marker1.getResource(); 356 IResource resource2 = marker2.getResource(); 357 358 IProject project1 = resource1.getProject(); 359 IProject project2 = resource2.getProject(); 360 int delta = project1.getName().compareTo(project2.getName()); 361 if (delta != 0) { 362 return delta; 363 } 364 365 delta = resource1.getName().compareTo(resource2.getName()); 366 if (delta != 0) { 367 return delta; 368 } 369 370 return marker1.getAttribute(IMarker.LINE_NUMBER, 0) 371 - marker2.getAttribute(IMarker.LINE_NUMBER, 0); 372 } 373 } 374 375 static class FileColumn extends LintColumn { FileColumn(LintList list)376 public FileColumn(LintList list) { 377 super(list); 378 } 379 380 @Override getColumnHeaderText()381 public @NonNull String getColumnHeaderText() { 382 return "File"; 383 } 384 385 @Override visibleByDefault()386 public boolean visibleByDefault() { 387 return false; 388 } 389 390 @Override getPreferredCharWidth()391 public int getPreferredCharWidth() { 392 return 12; 393 } 394 395 @Override getValue(@onNull IMarker marker)396 public String getValue(@NonNull IMarker marker) { 397 if (marker.getResource() instanceof IFile) { 398 return marker.getResource().getName(); 399 } else { 400 return ""; 401 } 402 } 403 } 404 405 static class PathColumn extends LintColumn { PathColumn(LintList list)406 public PathColumn(LintList list) { 407 super(list); 408 } 409 410 @Override getColumnHeaderText()411 public @NonNull String getColumnHeaderText() { 412 return "Path"; 413 } 414 415 @Override visibleByDefault()416 public boolean visibleByDefault() { 417 return false; 418 } 419 420 @Override getPreferredCharWidth()421 public int getPreferredCharWidth() { 422 return 25; 423 } 424 425 @Override getValue(@onNull IMarker marker)426 public String getValue(@NonNull IMarker marker) { 427 return marker.getResource().getFullPath().toOSString(); 428 } 429 } 430 431 static class LineColumn extends LintColumn { LineColumn(LintList list)432 public LineColumn(LintList list) { 433 super(list); 434 } 435 436 @Override getColumnHeaderText()437 public @NonNull String getColumnHeaderText() { 438 return "Line"; 439 } 440 441 @Override visibleByDefault()442 public boolean visibleByDefault() { 443 return false; 444 } 445 446 @Override isLeftAligned()447 public boolean isLeftAligned() { 448 return false; 449 } 450 451 @Override getPreferredCharWidth()452 public int getPreferredCharWidth() { 453 return 4; 454 } 455 456 @Override getValue(@onNull IMarker marker)457 public String getValue(@NonNull IMarker marker) { 458 int line = getLine(marker); 459 if (line >= 1) { 460 return Integer.toString(line); 461 } else { 462 return ""; 463 } 464 } 465 getLine(IMarker marker)466 private int getLine(IMarker marker) { 467 if (marker.getResource() instanceof IFile) { 468 int line = marker.getAttribute(IMarker.LINE_NUMBER, -1); 469 return line; 470 } 471 472 return -1; 473 } 474 @Override compare(IMarker marker1, IMarker marker2)475 public int compare(IMarker marker1, IMarker marker2) { 476 return getLine(marker1) - getLine(marker2); 477 } 478 } 479 480 static class PriorityColumn extends LintColumn { PriorityColumn(LintList list)481 public PriorityColumn(LintList list) { 482 super(list); 483 } 484 485 @Override getColumnHeaderText()486 public @NonNull String getColumnHeaderText() { 487 return "Priority"; 488 } 489 490 @Override visibleByDefault()491 public boolean visibleByDefault() { 492 return false; 493 } 494 495 @Override isLeftAligned()496 public boolean isLeftAligned() { 497 return false; 498 } 499 500 @Override getPreferredCharWidth()501 public int getPreferredCharWidth() { 502 return 2; 503 } 504 505 @Override getValue(@onNull IMarker marker)506 public String getValue(@NonNull IMarker marker) { 507 int priority = getPriority(marker); 508 if (priority > 0) { 509 return Integer.toString(priority); 510 } 511 return ""; 512 } 513 getPriority(IMarker marker)514 private int getPriority(IMarker marker) { 515 Issue issue = mList.getIssue(marker); 516 if (issue != null) { 517 return issue.getPriority(); 518 } 519 return 0; 520 } 521 522 @Override compare(IMarker marker1, IMarker marker2)523 public int compare(IMarker marker1, IMarker marker2) { 524 return getPriority(marker1) - getPriority(marker2); 525 } 526 527 @Override isAscending()528 public boolean isAscending() { 529 return false; 530 } 531 } 532 }