1 19 20 package org.netbeans.modules.java; 21 22 import java.lang.reflect.Modifier ; 23 import java.util.*; 24 import javax.swing.SwingUtilities ; 25 import javax.swing.text.StyledDocument ; 26 import javax.jmi.reflect.InvalidObjectException; 27 import org.netbeans.api.mdr.MDRObject; 28 import org.netbeans.api.mdr.events.AttributeEvent; 29 import org.netbeans.api.mdr.events.MDRChangeEvent; 30 import org.netbeans.api.mdr.events.MDRChangeListener; 31 import org.netbeans.api.mdr.events.TransactionEvent; 32 import org.netbeans.jmi.javamodel.*; 33 import org.netbeans.jmi.javamodel.ParameterizedType; 34 import org.netbeans.modules.java.settings.JavaSettings; 35 import org.netbeans.modules.javacore.internalapi.JavaMetamodel; 36 import org.netbeans.modules.javacore.JMManager; 37 import org.netbeans.modules.javacore.jmiimpl.javamodel.TypeClassImpl; 38 import org.openide.ErrorManager; 39 import org.openide.cookies.LineCookie; 40 import org.openide.text.Annotation; 41 import org.openide.text.Line; 42 import org.openide.util.RequestProcessor; 43 44 51 final class OverrideAnnotationSupport { 52 53 private final JavaEditor editor; 54 55 private WMDRChangeListener overriddenListener; 56 57 private Request currentRequest; 58 59 60 private List overrideAnnotations = new ArrayList (); 61 62 63 private boolean isSuspended = false; 64 65 private static final RequestProcessor QUEUE = new RequestProcessor("Overriddens Queue", 1); 67 public OverrideAnnotationSupport(JavaEditor editor) { 68 this.editor = editor; 69 this.overriddenListener = new WMDRChangeListener(this); 70 } 71 72 73 public void processOverriddenAnnotation() { 74 processOverriddenAnnotation(true); 75 } 76 77 private void processOverriddenAnnotation(boolean recompute) { 78 if (!isEnabled()) return; 79 synchronized(this) { 80 if (isSuspended) return; 81 if (currentRequest != null && !currentRequest.cancel()) { 82 currentRequest.followMe = true; 83 return; 84 } 85 currentRequest = new Request(); 86 QUEUE.post(currentRequest, 200); 88 } 89 } 90 91 92 public void suspend() { 93 synchronized(this) { 95 if (this.isSuspended) return; 96 this.isSuspended = true; 97 if (this.currentRequest != null && !this.currentRequest.cancel()) { 98 this.currentRequest.followMe = false; 100 } 101 } 102 103 Request clean = new Request(Request.CLEAN); 104 QUEUE.post(clean); 107 } 111 112 private void dispose() { 113 if (this.overriddenListener != null) { 114 this.overriddenListener.removeAllElements(); 115 } 116 detachAnnotations(this.overrideAnnotations); 117 this.overrideAnnotations.clear(); 118 } 119 120 121 private boolean isEnabled() { 122 return JavaSettings.getDefault().getShowOverriding(); 123 } 124 125 private void processOverriddenAnnotation(Resource rsc, boolean recompute) { 126 if (JMManager.PERF_DEBUG) Thread.dumpStack(); 128 final List originalAnnotations = recompute ? copyAnnotations() : this.overrideAnnotations; 129 final List overrideAnnotations = recompute ? this.findOverriddenMethods(rsc) : this.overrideAnnotations; 130 final List addedOverrideAnnotations = recompute ? new ArrayList (overrideAnnotations) : Collections.EMPTY_LIST; 131 final List removedOverrideAnnotations = recompute ? new ArrayList (originalAnnotations) : Collections.EMPTY_LIST; 132 final List unchangedOverrideAnnotations = recompute ? new ArrayList (originalAnnotations) : this.overrideAnnotations; 133 134 if (isSuspended) return; 135 136 if (recompute) { 137 addedOverrideAnnotations.removeAll(originalAnnotations); 138 removedOverrideAnnotations.removeAll(overrideAnnotations); 139 unchangedOverrideAnnotations.retainAll(overrideAnnotations); 140 detachAnnotations (removedOverrideAnnotations); 141 } 142 143 if (editor.isDocumentLoaded() && !(addedOverrideAnnotations.isEmpty() && overrideAnnotations.isEmpty())) { 144 StyledDocument doc = editor.getDocument(); 145 Runnable docRenderer = new Runnable () { 146 public void run() { 147 LineCookie cookie = (LineCookie) editor.getDataObject().getCookie(LineCookie.class); 148 Line.Set lines = cookie.getLineSet(); 149 for (Iterator it = addedOverrideAnnotations.iterator(); it.hasNext();) { 150 OverrideAnnotation ann = (OverrideAnnotation) it.next (); 151 ann.attachToLineSet(lines); 152 } 153 for (Iterator it = unchangedOverrideAnnotations.iterator(); it.hasNext();) { 154 OverrideAnnotation ann = (OverrideAnnotation) it.next(); 155 ann.updateLine(lines); 156 } 157 } 158 }; 159 if (doc != null) { 160 JavaMetamodel.getDefaultRepository().beginTrans(false); 161 try { 162 doc.render(docRenderer); 163 } finally { 164 JavaMetamodel.getDefaultRepository().endTrans(); 165 } 166 } else { 167 SwingUtilities.invokeLater (docRenderer); 168 } 169 } 170 List computedAnnotations = unchangedOverrideAnnotations; 171 computedAnnotations.addAll(addedOverrideAnnotations); 172 syncAnnotations(computedAnnotations); 173 } 174 175 private synchronized List copyAnnotations() { 176 return new ArrayList(this.overrideAnnotations); 177 } 178 179 private synchronized void syncAnnotations(List l) { 180 this.overrideAnnotations = l; 181 } 182 183 private void processOverriddenAnnotationImpl(boolean recompute) { 184 Resource rsc = editor.getResource(); 185 if (rsc != null) { 186 processOverriddenAnnotation(rsc, recompute); 187 } 188 } 189 190 private List findOverriddenMethods(JavaClass cls, Map methods) { 191 if (methods.isEmpty()) return Collections.EMPTY_LIST; 192 JavaClass parent; 193 List result = new ArrayList (); 194 List interfaces = new ArrayList (); 195 interfaces.addAll(cls.getInterfaces()); 196 parent = cls.getSuperClass(); 197 Set visited = new HashSet(); 198 while (parent != null && visited.add(parent)) { 199 if (isSuspended) return Collections.EMPTY_LIST; 200 if (Modifier.isFinal (parent.getModifiers())) { 201 break; 202 } 203 if (findOverridenMethods(parent, interfaces, methods, result)) return result; 204 cls = parent; 205 parent = cls.getSuperClass(); 206 } 207 while (!interfaces.isEmpty()) { 208 JavaClass ifc = (JavaClass) interfaces.remove(0); 209 if (visited.add(ifc)) { 210 if (findOverridenMethods(ifc, interfaces, methods, result)) return result; 211 } 212 } 213 return result; 214 } 215 216 private boolean findOverridenMethods(JavaClass parent, List interfaces, Map methods, List result) { 217 this.overriddenListener.addElement(parent); 218 interfaces.addAll(parent.getInterfaces()); 219 for (Iterator it = parent.getContents().iterator(); it.hasNext();) { 220 ClassMember tmp = (ClassMember) it.next(); 221 if (tmp instanceof Method) { 222 int modifiers = tmp.getModifiers(); 223 if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers) && (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers))) { 224 Method m = (Method) methods.get(tmp); 225 if (m != null) { 226 methods.remove (m); 227 result.add (new OverrideAnnotation.Descriptor((Method) tmp,m)); 228 if (methods.isEmpty()) { 229 return true; 231 } 232 } 233 } 234 } 235 } 236 return false; 237 } 238 239 private Map createMethodMap (JavaClass cls, Set classes) { 240 Map methods = new TreeMap (new Comparator() { 241 public int compare(Object o1, Object o2) { 242 Method m1 = (Method) o1, m2 = (Method) o2; 243 int result = m1.getName() == null ? -1 : m1.getName().compareTo(m2.getName()); 244 if (result == 0) { 245 List p1 = m1.getParameters(), p2 = m2.getParameters(); 246 Iterator it2 = p2.iterator(); 247 for (Iterator it1 = p1.iterator(); it1.hasNext() && result == 0;) { 248 Type param1 = ((Parameter) it1.next()).getType(); 249 if (it2.hasNext()) { 250 Type param2 = ((Parameter) it2.next()).getType(); 251 result = compareTypes(param1, param2); 252 } else { 253 result = -1; 254 } 255 } 256 if (result == 0 && it2.hasNext()) { 257 result = 1; 258 } 259 } 260 return result; 261 } 262 }); 263 for (Iterator it = cls.getContents().iterator(); it.hasNext();) { 264 ClassMember tmp = (ClassMember) it.next(); 265 if (tmp instanceof Method) { 266 int modifiers = tmp.getModifiers(); 267 if (!Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) { 268 methods.put (tmp, tmp); 269 } 270 } else if (tmp instanceof JavaClass) { 271 classes.add(tmp); 272 } 273 } 274 return methods; 275 } 276 277 private static int compareTypes(Type type1, Type type2) { 278 if (type1 == null || type1 instanceof UnresolvedClass || (type1 instanceof ParameterizedType && ((ParameterizedType) type1).getDefinition() instanceof UnresolvedClass)) return -1; 279 if (type2 == null || type2 instanceof UnresolvedClass || (type2 instanceof ParameterizedType && ((ParameterizedType) type2).getDefinition() instanceof UnresolvedClass)) return 1; 280 if (type1.equals(type2)) { 281 return 0; 282 } 283 type1=TypeClassImpl.getRawType(type1); 284 type2=TypeClassImpl.getRawType(type2); 285 return type1.getName() == null ? -1 : type1.getName().compareTo(type2.getName()); 286 } 287 288 private List findOverriddenMethods (Resource rsc) { 289 JavaMetamodel.getDefaultRepository().beginTrans(false); 290 try { 291 JMManager m=(JMManager)JavaMetamodel.getManager(); 292 m.setClassPath(rsc); 293 m.setSafeTrans(true); 294 this.overriddenListener.addElement(rsc); 295 Set classes = new HashSet(rsc.getClassifiers()); 296 List result = new ArrayList (); 297 while (!classes.isEmpty()) { 298 Iterator tmp = classes.iterator(); 299 JavaClass cls = (JavaClass) tmp.next(); 300 tmp.remove(); 301 this.overriddenListener.addElement(cls); 302 List methodsDescriptor = findOverriddenMethods(cls, createMethodMap(cls, classes)); 303 if (isSuspended) return result; 304 for (Iterator it = methodsDescriptor.iterator(); it.hasNext();) { 305 OverrideAnnotation.Descriptor descriptor = (OverrideAnnotation.Descriptor) it.next (); 306 result.add (OverrideAnnotation.forDescriptor (descriptor)); 307 } 308 } 309 return result; 310 } catch (InvalidObjectException e) { 311 return Collections.EMPTY_LIST; 313 } finally { 314 JavaMetamodel.getDefaultRepository().endTrans(); 315 } 316 } 317 318 private static void detachAnnotations(Collection anns) { 319 for (Iterator i = anns.iterator(); i.hasNext();) { 320 Annotation ann = (Annotation) i.next(); 321 try { 322 ann.detach(); 323 } catch (Exception e) { 324 ErrorManager.getDefault().notify(ErrorManager.WARNING, e); 325 } 326 } 327 } 328 329 330 private class Request implements Runnable { 331 private boolean isCanceled = false; 332 private boolean isRunning = false; 333 334 private boolean followMe = false; 335 336 private final int type; 337 private static final int DEFAULT = 0; 338 private static final int CLEAN = 1; 339 private static final int REFRESH = 2; 340 341 public Request() { 342 this(DEFAULT); 343 } 344 345 public Request(int type) { 346 this.type = type; 347 } 348 349 public void run() { 350 switch (type) { 351 case DEFAULT: 352 computeAnnotations(true); 353 break; 354 case REFRESH: 355 computeAnnotations(false); 356 break; 357 case CLEAN: 358 dispose(); 359 break; 360 default: assert false: "Invalid request type: " + type; } 362 } 363 364 private void computeAnnotations(boolean recompute) { 365 if (isCanceled) return; 366 try { 367 isRunning = true; 368 processOverriddenAnnotationImpl(recompute); 369 } finally { 370 isRunning = false; 371 } 372 373 synchronized(OverrideAnnotationSupport.this) { 374 if (followMe) { 375 followMe = false; 376 processOverriddenAnnotation(recompute); 377 } 378 } 379 } 380 381 384 public boolean cancel() { 385 isCanceled = true; 386 return !isRunning; 387 } 388 389 } 390 391 392 private static class WMDRChangeListener implements MDRChangeListener { 393 394 private boolean refresh = false, recompute = false; 395 OverrideAnnotationSupport support; 396 397 private Map containers; 399 private Resource rsc; 400 401 public WMDRChangeListener (OverrideAnnotationSupport support) { 402 this.support = support; 403 } 404 405 public synchronized void addElement (JavaClass cls) { 406 if (this.containers == null || support.isSuspended) { 407 this.containers = new HashMap (); 408 } 409 List methods = (List) this.containers.get(cls); 410 if (methods == null) { 411 methods = new ArrayList(); 412 for (Iterator it = cls.getContents().iterator(); it.hasNext();) { 413 Object tmp = it.next(); 414 if (tmp instanceof Method) { 415 methods.add(tmp); 416 ((MDRObject) tmp).addListener(this, AttributeEvent.EVENTMASK_ATTRIBUTE); 417 } 418 } 419 this.containers.put(cls, methods); 420 ((MDRObject) cls).addListener(this, AttributeEvent.EVENTMASK_ATTRIBUTE); 421 } 422 } 423 424 public synchronized void addElement (Resource rsc) { 425 this.rsc = rsc; 426 ((MDRObject) rsc).addListener(this, AttributeEvent.EVENTMASK_ATTRIBUTE); 427 JavaMetamodel.getDefaultRepository().addListener(this, TransactionEvent.EVENT_TRANSACTION_END); 428 } 429 430 private void updateMethods(JavaClass cls) { 431 JavaMetamodel.getDefaultRepository().beginTrans(false); 432 try { 433 synchronized (this) { 434 if (this.containers == null || support.isSuspended) 435 return; 436 List methods = (List) this.containers.get(cls); 437 if (methods != null) { 438 List toAdd = new ArrayList(); 439 List toRemove = new ArrayList(methods); 440 if (cls.isValid()) { 441 for (Iterator it = cls.getContents().iterator(); it.hasNext();) { 442 Object tmp = it.next(); 443 toRemove.remove(tmp); 444 if (tmp instanceof Method && !methods.contains(tmp)) { 445 toAdd.add(tmp); 446 ((MDRObject) tmp).addListener(this, AttributeEvent.EVENTMASK_ATTRIBUTE); 447 } 448 } 449 methods.addAll(toAdd); 450 } 451 methods.removeAll(toRemove); 452 for (Iterator it = toRemove.iterator(); it.hasNext();) { 453 MDRObject me = (MDRObject) it.next(); 454 me.removeListener(this); 455 } 456 } 457 } 458 } finally { 459 JavaMetamodel.getDefaultRepository().endTrans(); 460 } 461 } 462 463 public void removeAllElements () { 464 JavaMetamodel.getDefaultRepository().beginTrans(false); 465 try { 466 synchronized (this) { 467 if (this.rsc != null) { 468 ((MDRObject) rsc).removeListener(this); 469 JavaMetamodel.getDefaultRepository().removeListener(this); 470 rsc = null; 471 } 472 473 if (this.containers == null) 474 return; 475 476 for (Iterator it = this.containers.entrySet().iterator(); it.hasNext();) { 477 Map.Entry entry = (Map.Entry) it.next(); 478 MDRObject cls = (MDRObject) entry.getKey(); 479 List methods = (List) entry.getValue(); 480 cls.removeListener(this); 481 for (Iterator jt = methods.iterator(); jt.hasNext();) { 482 MDRObject m = (MDRObject) jt.next(); 483 m.removeListener(this); 484 } 485 methods.clear(); 486 } 487 this.containers.clear(); 488 } 489 } finally { 490 JavaMetamodel.getDefaultRepository().endTrans(); 491 } 492 } 493 494 public void change(MDRChangeEvent e) { 495 if (support.isSuspended) return; 496 497 if (e instanceof TransactionEvent) { 498 if (refresh) { 499 support.processOverriddenAnnotation(recompute); 500 refresh = recompute = false; 501 } 502 return; 503 } 504 505 if ((e.getSource() instanceof Element) && !((Element) e.getSource()).isValid()) { 506 return ; 507 } 508 509 AttributeEvent event = (AttributeEvent) e; 510 String attrName = event.getAttributeName(); 511 512 refresh = true; 513 514 if (event.getSource() instanceof JavaClass) { 515 if ("contents".equals(attrName) && ((event.getNewElement() instanceof Method) || (event.getOldElement() instanceof Method))) { updateMethods((JavaClass) event.getSource()); 517 recompute = true; 518 } else if ("superClassName".equals(attrName) || "interfaceNames".equals(attrName)) { if (this.containers.containsKey(event.getSource())) { 520 this.removeAllElements(); 521 } 522 recompute = true; 523 } 524 } else if (event.getSource() instanceof Method) { 525 if ("name".equals(attrName) || "modifiers".equals(attrName) || "parameters".equals(attrName) || "typeName".equals(attrName)) { recompute = true; 527 } 528 } 529 530 } 532 } 533 } 534 | Popular Tags |