KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > OverrideAnnotationSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.java;
21
22 import java.lang.reflect.Modifier JavaDoc;
23 import java.util.*;
24 import javax.swing.SwingUtilities JavaDoc;
25 import javax.swing.text.StyledDocument JavaDoc;
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 /**
45  * The main purpose of this class is to manage annotations of overridden and implemented methods for a particular
46  * JavaEditor. Unused instances should release resources by {@link #suspend}. The suspended instance should not be
47  * reused.
48  *
49  * @author Jan Pokorsky, Tomas Zezula
50  */

51 final class OverrideAnnotationSupport {
52     
53     private final JavaEditor editor;
54     
55     private WMDRChangeListener overriddenListener;
56     
57     private Request currentRequest;
58     
59     /* List of override attached to this document */
60     private List overrideAnnotations = new ArrayList ();
61     
62     /** model was cleared so do not try to do anything with it */
63     private boolean isSuspended = false;
64
65     private static final RequestProcessor QUEUE = new RequestProcessor("Overriddens Queue", 1); // NOI18N
66

67     public OverrideAnnotationSupport(JavaEditor editor) {
68         this.editor = editor;
69         this.overriddenListener = new WMDRChangeListener(this);
70     }
71     
72     /** computes annotations and attaches them. */
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             // schedule the request with a delay to prevent excessive cpu consumption in case of flood of requests
87
QUEUE.post(currentRequest, 200);
88         }
89     }
90     
91     /** stops processing, unregisters listeners, detaches annotations */
92     public void suspend() {
93 // boolean isRunning = false;
94
synchronized(this) {
95             if (this.isSuspended) return;
96             this.isSuspended = true;
97             if (this.currentRequest != null && !this.currentRequest.cancel()) {
98 // isRunning = true;
99
this.currentRequest.followMe = false;
100             }
101         }
102         
103         Request clean = new Request(Request.CLEAN);
104 // if (isRunning) { // do not block
105
// MaM - isRunning commented out to always perform this in the QUEUE to prevent deadlocks (#46115)
106
QUEUE.post(clean);
107 // } else {
108
// clean.run();
109
// }
110
}
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     /** show annotations? */
121     private boolean isEnabled() {
122         return JavaSettings.getDefault().getShowOverriding();
123     }
124         
125     private void processOverriddenAnnotation(Resource rsc, boolean recompute) {
126 // System.err.println("### DO RECOMPUTE OVERRIDENS: " + editor.getDataObject().getPrimaryFile() + ", " + Thread.currentThread());
127
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 JavaDoc doc = editor.getDocument();
145             Runnable JavaDoc docRenderer = new Runnable JavaDoc() {
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                             //Fully covered
230
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 JavaDoc o1, Object JavaDoc 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             // ignore
312
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 JavaDoc e) {
324                 ErrorManager.getDefault().notify(ErrorManager.WARNING, e);
325             }
326         }
327     }
328         
329     /** represents sheduled reqest to process overridden annotations */
330     private class Request implements Runnable JavaDoc {
331         private boolean isCanceled = false;
332         private boolean isRunning = false;
333         /** follow current reqest with a new one when it is finished */
334         private boolean followMe = false;
335         /** request type */
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; // NOI18N
361
}
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         /**
382          * @return true if canceled
383          */

384         public boolean cancel() {
385             isCanceled = true;
386             return !isRunning;
387         }
388             
389     }
390     
391     /** listens to changes of class, superclasses, interfaces and all their methods */
392     private static class WMDRChangeListener implements MDRChangeListener {
393
394         private boolean refresh = false, recompute = false;
395         OverrideAnnotationSupport support;
396
397         //Map<ClassElement, List<MethodElement>>
398
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 JavaDoc 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 JavaDoc 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 JavaDoc 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))) { // NOI18N
516
updateMethods((JavaClass) event.getSource());
517                     recompute = true;
518                 } else if ("superClassName".equals(attrName) || "interfaceNames".equals(attrName)) { // NOI18N
519
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)) { // NOI18N
526
recompute = true;
527                 }
528             }
529             
530             // todo: handle the situation when a parameter type is changed
531
}
532     }
533 }
534
Popular Tags