1 19 20 package org.netbeans.modules.editor.errorstripe; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.io.IOException ; 25 import java.util.ArrayList ; 26 import java.util.Collection ; 27 import java.util.Collections ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.SortedMap ; 31 import java.util.TreeMap ; 32 import javax.swing.text.JTextComponent ; 33 import org.netbeans.editor.AnnotationDesc; 34 import org.netbeans.editor.AnnotationType; 35 import org.netbeans.editor.AnnotationType.Severity; 36 import org.netbeans.editor.Annotations; 37 import org.netbeans.editor.BaseDocument; 38 import org.netbeans.editor.BaseKit; 39 import org.netbeans.editor.Utilities; 40 import org.netbeans.modules.editor.errorstripe.privatespi.Mark; 41 import org.netbeans.modules.editor.errorstripe.privatespi.MarkProvider; 42 import org.netbeans.modules.editor.errorstripe.privatespi.MarkProviderCreator; 43 import org.netbeans.modules.editor.errorstripe.privatespi.Status; 44 import org.netbeans.spi.editor.errorstripe.UpToDateStatus; 45 import org.netbeans.spi.editor.errorstripe.UpToDateStatusProvider; 46 import org.netbeans.spi.editor.errorstripe.UpToDateStatusProviderFactory; 47 import org.openide.ErrorManager; 48 import org.openide.filesystems.FileObject; 49 import org.openide.filesystems.Repository; 50 import org.openide.loaders.DataFolder; 51 import org.openide.loaders.DataObject; 52 import org.openide.loaders.FolderLookup; 53 import org.openide.util.Lookup; 54 import org.openide.util.Lookup.Result; 55 import org.openide.util.Lookup.Template; 56 import org.openide.util.lookup.ProxyLookup; 57 import org.netbeans.modules.editor.errorstripe.apimodule.SPIAccessor; 58 59 63 final class AnnotationViewDataImpl implements PropertyChangeListener , AnnotationViewData, Annotations.AnnotationsListener { 64 65 private static final ErrorManager ERR = AnnotationView.ERR; 66 67 private AnnotationView view; 68 private JTextComponent pane; 69 private BaseDocument document; 70 71 private List providers = new ArrayList (); 72 private List upToDateStatusProviders = new ArrayList (); 73 74 private List currentMarks = null; 75 private SortedMap marksMap = null; 76 77 78 public AnnotationViewDataImpl(AnnotationView view, JTextComponent pane) { 79 this.view = view; 80 this.pane = pane; 81 this.document = null; 82 } 83 84 public void register(BaseDocument document) { 85 this.document = document; 86 87 gatherProviders(pane); 88 addListenersToProviders(); 89 90 if (document != null) { 91 document.getAnnotations().addAnnotationsListener(this); 92 } 93 94 currentMarks = null; 95 marksMap = null; 96 } 97 98 public void unregister() { 99 if (document != null) { 100 document.getAnnotations().removeAnnotationsListener(this); 101 } 102 103 removeListenersFromProviders(); 104 105 document = null; 106 } 107 108 private void gatherProviders(JTextComponent pane) { 109 long start = System.currentTimeMillis(); 110 try { 111 BaseKit kit = Utilities.getKit(pane); 112 113 if (kit == null) 114 return ; 115 116 String content = kit.getContentType(); 117 BaseDocument document = (BaseDocument) pane.getDocument(); 118 FileObject baseFolder = Repository.getDefault().getDefaultFileSystem().findResource("Editors/text/base/UpToDateStatusProvider"); FileObject contentFolder = Repository.getDefault().getDefaultFileSystem().findResource("Editors/" + content + "/UpToDateStatusProvider"); 121 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 122 ERR.log(ErrorManager.INFORMATIONAL, "baseFolder = " + baseFolder ); 123 } 124 125 DataObject baseDO = baseFolder != null ? DataObject.find(baseFolder) : null; 126 127 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 128 ERR.log(ErrorManager.INFORMATIONAL, "baseDO = " + baseDO ); 129 } 130 131 Lookup baseLookup = baseFolder != null ? new FolderLookup((DataFolder) baseDO).getLookup() : Lookup.EMPTY; 132 133 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 134 ERR.log(ErrorManager.INFORMATIONAL, "contentFolder = " + contentFolder ); 135 } 136 137 DataObject contentDO = contentFolder != null ? DataObject.find(contentFolder) : null; 138 Lookup contentLookup = contentFolder != null ? new FolderLookup((DataFolder) contentDO).getLookup() : Lookup.EMPTY; 139 140 Lookup lookup = new ProxyLookup(new Lookup[] {baseLookup, contentLookup}); 141 142 Result creators = lookup.lookup(new Template(MarkProviderCreator.class)); 143 144 List markProviders = new ArrayList (); 145 146 for (Iterator i = creators.allInstances().iterator(); i.hasNext(); ) { 147 MarkProviderCreator creator = (MarkProviderCreator) i.next(); 148 149 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 150 ERR.log(ErrorManager.INFORMATIONAL, "creator = " + creator ); 151 } 152 153 MarkProvider provider = creator.createMarkProvider(pane); 154 155 if (provider != null) 156 markProviders.add(provider); 157 } 158 159 this.providers = markProviders; 160 161 Result updsCreators = lookup.lookup(new Template(UpToDateStatusProviderFactory.class)); 162 List updsProviders = new ArrayList (); 163 164 for (Iterator i = updsCreators.allInstances().iterator(); i.hasNext(); ) { 165 UpToDateStatusProviderFactory creator = (UpToDateStatusProviderFactory) i.next(); 166 167 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 168 ERR.log(ErrorManager.INFORMATIONAL, "creator = " + creator ); 169 } 170 171 UpToDateStatusProvider provider = creator.createUpToDateStatusProvider(pane.getDocument()); 172 173 if (provider != null) 174 updsProviders.add(provider); 175 } 176 177 this.upToDateStatusProviders = updsProviders; 178 } catch (IOException e) { 179 ErrorManager.getDefault().notify(e); 180 } 181 182 long end = System.currentTimeMillis(); 183 184 if (AnnotationView.TIMING_ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 185 AnnotationView.TIMING_ERR.log(ErrorManager.INFORMATIONAL, "gather providers took: " + (end - start)); 186 } 187 } 188 189 private void addListenersToProviders() { 190 for (Iterator p = upToDateStatusProviders.iterator(); p.hasNext(); ) { 191 UpToDateStatusProvider provider = (UpToDateStatusProvider) p.next(); 192 193 SPIAccessor.getDefault().addPropertyChangeListener(provider, this); 194 } 195 196 for (Iterator p = providers.iterator(); p.hasNext(); ) { 197 MarkProvider provider = (MarkProvider) p.next(); 198 199 provider.addPropertyChangeListener(this); 200 } 201 } 202 203 private void removeListenersFromProviders() { 204 for (Iterator p = upToDateStatusProviders.iterator(); p.hasNext(); ) { 205 UpToDateStatusProvider provider = (UpToDateStatusProvider) p.next(); 206 207 SPIAccessor.getDefault().removePropertyChangeListener(provider, this); 208 } 209 210 for (Iterator p = providers.iterator(); p.hasNext(); ) { 211 MarkProvider provider = (MarkProvider) p.next(); 212 213 provider.removePropertyChangeListener(this); 214 } 215 } 216 217 static List createMergedMarks(List providers) { 218 List result = new ArrayList (); 219 220 for (Iterator p = providers.iterator(); p.hasNext(); ) { 221 MarkProvider provider = (MarkProvider) p.next(); 222 223 result.addAll(provider.getMarks()); 224 } 225 226 return result; 227 } 228 229 synchronized List getMergedMarks() { 230 if (currentMarks == null) { 231 currentMarks = createMergedMarks(providers); 232 } 233 234 return currentMarks; 235 } 236 237 static List getStatusesForLineImpl(int line, SortedMap marks) { 238 List inside = (List ) marks.get(new Integer (line)); 239 240 if (inside == null) 241 return Collections.EMPTY_LIST; 242 243 return inside; 244 } 245 246 public Mark getMainMarkForBlock(int startLine, int endLine) { 247 Mark m1 = getMainMarkForBlockImpl(startLine, endLine, getMarkMap()); 248 Mark m2 = getMainMarkForBlockAnnotations(startLine, endLine); 249 250 if (m1 == null) 251 return m2; 252 253 if (m2 == null) 254 return m1; 255 256 if (isMoreImportant(m1, m2)) 257 return m1; 258 else 259 return m2; 260 } 261 262 static Mark getMainMarkForBlockImpl(int startLine, int endLine, SortedMap marks) { 263 int current = startLine - 1; 264 Mark found = null; 265 266 while ((current = findNextUsedLine(current, marks)) != Integer.MAX_VALUE && current <= endLine) { 267 for (Iterator i = getStatusesForLineImpl(current, marks).iterator(); i.hasNext(); ) { 268 Mark newMark = (Mark) i.next(); 269 270 if (found == null || isMoreImportant(newMark, found)) { 271 found = newMark; 272 } 273 } 274 } 275 276 return found; 277 } 278 279 private static boolean isMoreImportant(Mark m1, Mark m2) { 280 int compared = m1.getStatus().compareTo(m2.getStatus()); 281 282 if (compared == 0) 283 return m1.getPriority() < m2.getPriority(); 284 285 return compared > 0; 286 } 287 288 private boolean isMoreImportant(AnnotationDesc a1, AnnotationDesc a2) { 289 AnnotationType t1 = a1.getAnnotationTypeInstance(); 290 AnnotationType t2 = a2.getAnnotationTypeInstance(); 291 292 int compared = t1.getSeverity().compareTo(t2.getSeverity()); 293 294 if (compared == 0) 295 return t1.getPriority() < t2.getPriority(); 296 297 return compared > 0; 298 } 299 300 private boolean isValidForErrorStripe(AnnotationDesc a) { 301 return a.getAnnotationTypeInstance().getSeverity() != AnnotationType.Severity.STATUS_NONE; 302 } 303 304 private Mark getMainMarkForBlockAnnotations(int startLine, int endLine) { 305 int line = startLine; 306 AnnotationDesc foundDesc = null; 307 Annotations annotations = document.getAnnotations(); 308 309 while ((line = annotations.getNextLineWithAnnotation(line)) <= endLine && line != (-1)) { 310 AnnotationDesc desc = annotations.getActiveAnnotation(line); 311 312 if (desc != null) { 313 if ((foundDesc == null || isMoreImportant(desc, foundDesc)) && isValidForErrorStripe(desc)) 314 foundDesc = desc; 315 } 316 317 if (annotations.getNumberOfAnnotations(line) > 1) { 318 AnnotationDesc[] descriptions = annotations.getPasiveAnnotations(line); 319 320 for (int cntr = 0; cntr < descriptions.length; cntr++) { 321 if ((foundDesc == null || isMoreImportant(descriptions[cntr], foundDesc)) && isValidForErrorStripe(descriptions[cntr])) 322 foundDesc = descriptions[cntr]; 323 } 324 } 325 326 line++; 327 } 328 329 if (foundDesc != null) 330 return new AnnotationMark(foundDesc); 331 else 332 return null; 333 } 334 335 public int findNextUsedLine(int from) { 336 int line1 = findNextUsedLine(from, getMarkMap()); 337 int line2 = document.getAnnotations().getNextLineWithAnnotation(from + 1); 338 339 if (line2 == (-1)) 340 line2 = Integer.MAX_VALUE; 341 342 return line1 < line2 ? line1 : line2; 343 } 344 345 static int findNextUsedLine(int from, SortedMap marks) { 346 SortedMap next = marks.tailMap(new Integer (from + 1)); 347 348 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 349 ERR.log("AnnotationView.findNextUsedLine from: " + from); 350 ERR.log("AnnotationView.findNextUsedLine marks: " + marks); 351 ERR.log("AnnotationView.findNextUsedLine next: " + next); 352 } 353 354 if (next.isEmpty()) { 355 return Integer.MAX_VALUE; 356 } 357 358 Integer nextLine = (Integer ) next.firstKey(); 359 360 return nextLine.intValue(); 361 } 362 363 private void registerMark(Mark mark) { 364 int[] span = mark.getAssignedLines(); 365 366 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 367 ERR.log("AnnotationView.registerMark mark: " + mark); 368 ERR.log("AnnotationView.registerMark lines from-to: " + span[0] + "-" + span[1]); 369 } 370 371 for (int line = span[0]; line <= span[1]; line++) { 372 Integer lineInt = new Integer (line); 373 374 List inside = (List ) marksMap.get(lineInt); 375 376 if (inside == null) { 377 marksMap.put(lineInt, inside = new ArrayList ()); 378 } 379 380 inside.add(mark); 381 } 382 } 383 384 private void unregisterMark(Mark mark) { 385 int[] span = mark.getAssignedLines(); 386 387 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 388 ERR.log("AnnotationView.unregisterMark mark: " + mark); 389 ERR.log("AnnotationView.unregisterMark lines from-to: " + span[0] + "-" + span[1]); 390 } 391 392 for (int line = span[0]; line <= span[1]; line++) { 393 Integer lineInt = new Integer (line); 394 395 List inside = (List ) marksMap.get(lineInt); 396 397 if (inside != null) { 398 inside.remove(mark); 399 400 if (inside.size() == 0) { 401 marksMap.remove(lineInt); 402 } 403 } 404 } 405 } 406 407 synchronized SortedMap getMarkMap() { 408 if (marksMap == null) { 409 List marks = getMergedMarks(); 410 marksMap = new TreeMap (); 411 412 for (Iterator i = marks.iterator(); i.hasNext(); ) { 413 Mark mark = (Mark) i.next(); 414 415 registerMark(mark); 416 } 417 } 418 419 return marksMap; 420 } 421 422 public Status computeTotalStatus() { 423 Status targetStatus = Status.STATUS_OK; 424 Collection marks = getMergedMarks(); 425 426 for (Iterator m = marks.iterator(); m.hasNext(); ) { 427 Mark mark = (Mark) m.next(); 428 Status s = mark.getStatus(); 429 430 targetStatus = Status.getCompoundStatus(s, targetStatus); 431 } 432 433 Annotations annotations = document.getAnnotations(); 434 int line = -1; 435 436 while ((line = annotations.getNextLineWithAnnotation(line)) != (-1)) { 437 AnnotationDesc desc = annotations.getActiveAnnotation(line); 438 439 if (desc != null) { 440 Status s = get(desc.getAnnotationTypeInstance()); 441 442 if (s != null) 443 targetStatus = Status.getCompoundStatus(s, targetStatus); 444 } 445 446 if (annotations.getNumberOfAnnotations(line) > 1) { 447 AnnotationDesc[] descriptions = annotations.getPasiveAnnotations(line); 448 449 for (int cntr = 0; cntr < descriptions.length; cntr++) { 450 Status s = get(descriptions[cntr].getAnnotationTypeInstance()); 451 452 if (s != null) 453 targetStatus = Status.getCompoundStatus(s, targetStatus); 454 } 455 } 456 457 line++; 458 } 459 460 return targetStatus; 461 } 462 463 public UpToDateStatus computeTotalStatusType() { 464 if (upToDateStatusProviders.isEmpty()) 465 return UpToDateStatus.UP_TO_DATE_DIRTY; 466 467 UpToDateStatus statusType = UpToDateStatus.UP_TO_DATE_OK; 468 469 for (Iterator p = upToDateStatusProviders.iterator(); p.hasNext(); ) { 470 UpToDateStatusProvider provider = (UpToDateStatusProvider) p.next(); 471 UpToDateStatus newType = provider.getUpToDate(); 472 473 if (newType.compareTo(statusType) > 0) { 474 statusType = newType; 475 } 476 } 477 478 return statusType; 479 } 480 481 public void propertyChange(PropertyChangeEvent evt) { 482 if ("marks".equals(evt.getPropertyName())) { 483 synchronized (this) { 484 Collection nue = (Collection ) evt.getNewValue(); 485 Collection old = (Collection ) evt.getOldValue(); 486 487 if (nue == null && evt.getSource() instanceof MarkProvider) 488 nue = ((MarkProvider) evt.getSource()).getMarks(); 489 490 if (old != null && nue != null) { 491 List added = new ArrayList (nue); 492 List removed = new ArrayList (old); 493 494 added.removeAll(old); 495 removed.removeAll(nue); 496 497 if (marksMap != null) { 498 for (Iterator i = removed.iterator(); i.hasNext(); ) { 499 unregisterMark((Mark) i.next()); 500 } 501 502 for (Iterator i = added.iterator(); i.hasNext(); ) { 503 registerMark((Mark) i.next()); 504 } 505 } 506 507 if (currentMarks != null) { 508 currentMarks.removeAll(removed); 509 currentMarks.addAll(added); 510 } 511 512 view.fullRepaint(); 513 } else { 514 ErrorManager.getDefault().log(ErrorManager.WARNING, "For performance reasons, the providers should fill both old and new value in property changes. Problematic event: " + evt); 515 clear(); 516 view.fullRepaint(); 517 } 518 return ; 519 } 520 } 521 522 if (UpToDateStatusProvider.PROP_UP_TO_DATE.equals(evt.getPropertyName())) { 523 view.fullRepaint(false); 524 return ; 525 } 526 } 527 528 public void clear() { 529 currentMarks = null; 530 marksMap = null; 531 } 532 533 public int[] computeErrorsAndWarnings() { 534 int errors = 0; 535 int warnings = 0; 536 Collection marks = getMergedMarks(); 537 538 for (Iterator m = marks.iterator(); m.hasNext(); ) { 539 Mark mark = (Mark) m.next(); 540 Status s = mark.getStatus(); 541 542 errors += s == Status.STATUS_ERROR ? 1 : 0; 543 warnings += s == Status.STATUS_WARNING ? 1 : 0; 544 } 545 546 Annotations annotations = document.getAnnotations(); 547 int line = -1; 548 549 while ((line = annotations.getNextLineWithAnnotation(line)) != (-1)) { 550 AnnotationDesc desc = annotations.getActiveAnnotation(line); 551 552 if (desc != null) { 553 Status s = get(desc.getAnnotationTypeInstance()); 554 555 if (s != null) { 556 errors += s == Status.STATUS_ERROR ? 1 : 0; 557 warnings += s == Status.STATUS_WARNING ? 1 : 0; 558 } 559 } 560 561 if (annotations.getNumberOfAnnotations(line) > 1) { 562 AnnotationDesc[] descriptions = annotations.getPasiveAnnotations(line); 563 564 for (int cntr = 0; cntr < descriptions.length; cntr++) { 565 Status s = get(descriptions[cntr].getAnnotationTypeInstance()); 566 567 if (s != null) { 568 errors += s == Status.STATUS_ERROR ? 1 : 0; 569 warnings += s == Status.STATUS_WARNING ? 1 : 0; 570 } 571 } 572 } 573 574 line++; 575 } 576 577 return new int[] {errors, warnings}; 578 } 579 580 public void changedLine(int Line) { 581 changedAll(); 582 } 583 584 public void changedAll() { 585 view.fullRepaint(false); 586 } 587 588 static Status get(Severity severity) { 589 if (severity == Severity.STATUS_ERROR) 590 return Status.STATUS_ERROR; 591 if (severity == Severity.STATUS_WARNING) 592 return Status.STATUS_WARNING; 593 if (severity == Severity.STATUS_OK) 594 return Status.STATUS_OK; 595 596 return null; 597 } 598 599 static Status get(AnnotationType ann) { 600 return get(ann.getSeverity()); 601 } 602 } 603 | Popular Tags |