KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > tools > JMIInheritanceSupport


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.tools;
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.beans.PropertyChangeSupport JavaDoc;
24 import java.lang.reflect.Modifier JavaDoc;
25 import java.util.*;
26 import javax.jmi.reflect.InvalidObjectException;
27 import org.netbeans.api.mdr.MDRepository;
28 import org.netbeans.jmi.javamodel.Resource;
29 import org.netbeans.jmi.javamodel.ClassDefinition;
30 import org.netbeans.jmi.javamodel.JavaClass;
31 import org.netbeans.jmi.javamodel.JavaModelPackage;
32 import org.netbeans.jmi.javamodel.Method;
33 import org.netbeans.jmi.javamodel.NamedElement;
34 import org.netbeans.jmi.javamodel.Parameter;
35 import org.netbeans.jmi.javamodel.ParameterizedType;
36 import org.netbeans.jmi.javamodel.PrimitiveType;
37 import org.netbeans.jmi.javamodel.PrimitiveTypeKindEnum;
38 import org.netbeans.jmi.javamodel.Type;
39 import org.netbeans.modules.java.settings.JavaSynchronizationSettings;
40 import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
41 import org.netbeans.modules.javacore.internalapi.JavaModelUtil;
42 import org.netbeans.modules.javacore.jmiimpl.javamodel.MethodImpl;
43 import org.netbeans.modules.javacore.jmiimpl.javamodel.ParameterizedTypeImpl;
44 import org.openide.ErrorManager;
45 import org.openide.util.NbBundle;
46 import org.openide.util.RequestProcessor;
47 import org.openide.util.SharedClassObject;
48 import org.openide.util.Task;
49 import org.openide.util.TaskListener;
50 import org.openide.loaders.DataObject;
51
52 public class JMIInheritanceSupport implements Runnable JavaDoc, TaskListener {
53     
54     public static final String JavaDoc PROP_FINISHED = "JMIInheritanceSupport.finished"; //NOI18N
55

56     private static String JavaDoc getString(String JavaDoc key) {
57         return NbBundle.getMessage(JMIInheritanceSupport.class, key);
58     }
59     
60     private ClassDefinition root;
61     
62     private JavaModelPackage modelPackage;
63     
64     private Resource resource;
65
66     private Set classes;
67     
68     // <MethodElement.Key, MethodElement>
69
private Map methods;
70     
71     // <Identifier, Set<MethodElement.Key>>, (alias <class, Set<method>>)
72
private Map tree;
73     
74     private List classesList;
75     
76     private Set finalMethodsKeys;
77     
78     private int ordNumCounter;
79     
80     private PropertyChangeSupport JavaDoc pcs = new PropertyChangeSupport JavaDoc(this);
81     private RequestProcessor.Task task = RequestProcessor.getDefault().create(this);
82     
83     /** used for storing info about position where to add next feature */
84     private int nextPosition = -1;
85     
86     /** Creates a new instance of JMIInheritanceSupport. The root must be a class. */
87     public JMIInheritanceSupport(ClassDefinition root) {
88         DataObject dobj = (DataObject) JavaMetamodel.getManager().getDataObject(root.getResource());
89         assert dobj != null;
90         this.root = root;
91         this.resource = root.getResource();
92         this.modelPackage = (JavaModelPackage)root.refOutermostPackage();
93         this.task.addTaskListener(this);
94     }
95
96     public ClassDefinition getRootClass() {
97         return root;
98     }
99     
100     public void setRootClass(ClassDefinition root) {
101         this.root = root;
102     }
103     
104     public Collection getClasses(Collection result, boolean cls, boolean ifc) {
105         if (!task.isFinished()) {
106             task.waitFinished();
107         }
108
109         for (Iterator i = classesList.iterator(); i.hasNext(); ) {
110             JavaClass jc = (JavaClass) i.next();
111             boolean isInterface = jc.isInterface();
112             if ((!isInterface && cls) || (isInterface && ifc)) {
113                 result.add(jc);
114             }
115         }
116         
117         return result;
118     }
119     
120     public Collection getAllMethods(Collection result, boolean abstractOnly) {
121         if (!task.isFinished()) {
122             task.waitFinished();
123         }
124
125         for (Iterator i = methods.values().iterator(); i.hasNext(); ) {
126             Method mte = (Method) i.next();
127             if (!abstractOnly || (isAbstract(mte) && !isImplemented(mte))) {
128                 result.add(mte);
129             }
130         }
131         
132         return result;
133     }
134     
135     public Collection getMethods(Collection result, ClassDefinition cd, boolean abstractOnly) {
136         if (!task.isFinished()) {
137             task.waitFinished();
138         }
139
140         Set meths = (Set) tree.get(cd);
141         for (Iterator i = meths.iterator(); i.hasNext(); ) {
142             MethodKey key = (MethodKey) i.next();
143             Method m = (Method) methods.get(key);
144             if (!abstractOnly || (isAbstract(m) && !isImplemented(m))) {
145                 result.add(m);
146             }
147         }
148         
149         return result;
150     }
151
152     public Collection sortMethods(Map selection) {
153         int size = selection.size();
154         List list = new ArrayList(size);
155         Map map = new HashMap();
156         for (Iterator iter = methods.keySet().iterator(); iter.hasNext();) {
157             Object JavaDoc val = iter.next();
158             map.put(val, val);
159         }
160         for (Iterator iter = selection.entrySet().iterator(); iter.hasNext();) {
161             Map.Entry entry = (Map.Entry)iter.next();
162             int ord = ((MethodKey)map.get(entry.getKey())).getOrdNumber();
163             list.add(new MethodRecord((Method)entry.getValue(), ord));
164         }
165         Collections.sort(list, new MethodComparator());
166         List result = new ArrayList(size);
167         for (Iterator iter = list.iterator(); iter.hasNext();) {
168             result.add(((MethodRecord)iter.next()).method);
169         }
170         return result;
171     }
172     
173     /**
174      * Adds method that overrides <code>source</code>.
175      * @param source method to be overridden
176      * @param superCall generate super call?
177      * @param javadoc generate super javadoc?
178      * @return reference to method created in source file
179      * @throws SourceException impossible to add the method
180      */

181     public Method overrideMethod(Method m, boolean superCall, boolean javadoc) {
182         try {
183             String JavaDoc bodyText = "";
184
185             // remove ABSTRACT and NATIVE since the user probably wants to write the method body ;-)
186
// SYNCHRONIZED is removed for safety reasons: if the user wants the method to be synchronized,
187
// she will add the modifier.
188
int mods = m.getModifiers() & ~(Modifier.NATIVE | Modifier.ABSTRACT | Modifier.SYNCHRONIZED);
189             // to be formatted nicely in the source code...
190
if (superCall) {
191                 bodyText = createSuperCall(m, isImplementedInSuper(root, m, false));
192             }
193             MethodImpl miOrig;
194             
195             if (m instanceof MethodImpl)
196                 miOrig = (MethodImpl) m;
197             else
198                 miOrig = ((MethodImpl) ((ParameterizedTypeImpl.Wrapper) m).getWrappedObject());
199             
200             MethodImpl newMethod = (MethodImpl) miOrig.duplicate((JavaModelPackage) root.refImmediatePackage());
201             newMethod.setJavadocText(javadoc?miOrig.getJavadocText():null);
202             newMethod.setModifiers(mods);
203             newMethod.setBody(null);
204             newMethod.fixImports(root, m);
205             newMethod.setBodyText(bodyText);
206             addMethod(newMethod);
207             return newMethod;
208         } catch (InvalidObjectException e) {
209             ErrorManager.getDefault().notify(e);
210             return null;
211         }
212     }
213     
214     private void addMethod(Method m) {
215         List contents = root.getContents();
216         if (nextPosition != -1) {
217             contents.add(nextPosition, m);
218             nextPosition++;
219             return;
220         }
221         int index = -1;
222         ListIterator iter = contents.listIterator();
223         for (int x = 0; iter.hasNext(); x++) {
224             Object JavaDoc obj = iter.next();
225             if (!(obj instanceof JavaClass)) {
226                 index = x;
227             }
228         }
229         if (index == -1) {
230             contents.add(m);
231             nextPosition = 1;
232             return;
233         }
234         
235         JavaMetamodel manager = JavaMetamodel.getManager();
236         iter = contents.listIterator(index + 1);
237         while (iter.hasPrevious()) {
238             NamedElement elem = (NamedElement) iter.previous();
239             if (!manager.isElementGuarded(elem)) {
240                 iter.next();
241                 iter.add(m);
242                 nextPosition = contents.indexOf(m) + 1;
243                 return;
244             }
245         }
246         
247         contents.add(0, m);
248         nextPosition = 1;
249     }
250     
251     public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
252         pcs.addPropertyChangeListener(l);
253     }
254
255     public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
256         pcs.removePropertyChangeListener(l);
257     }
258
259     public boolean isFinished() {
260         return task.isFinished();
261     }
262     
263     public void reset() {
264         classes = null;
265         classesList = null;
266         methods = null;
267         tree = null;
268         finalMethodsKeys = null;
269         ordNumCounter = 0;
270         task.schedule(0);
271     }
272
273     // ---- Runnable implementation
274

275     public void run() {
276         classes = new HashSet();
277         classesList = new ArrayList();
278         methods = new HashMap();
279         tree = new HashMap();
280         finalMethodsKeys = new HashSet();
281         ordNumCounter = 0;
282
283         MDRepository repository = JavaMetamodel.getDefaultRepository();
284         repository.beginTrans(false);
285         try {
286             JavaMetamodel model = JavaMetamodel.getManager();
287             model.setClassPath(model.getFileObject(root.getResource()), true);
288             
289             // traverse all super classes
290
JavaClass superClass = root.getSuperClass();
291             while (superClass != null && classes.add(superClass)) {
292                 classesList.add(superClass);
293                 HashSet meths = new HashSet();
294                 collectMethods(meths, superClass);
295                 tree.put(superClass, meths);
296                 superClass = superClass.getSuperClass();
297             }
298
299             // traverse all interfaces
300
traverseInterfaces(classes, classesList, tree, root);
301         } finally {
302             repository.endTrans();
303         }
304     }
305
306     private void traverseInterfaces(Set classes, List classesList, Map tree, ClassDefinition root) {
307         List interfaces = root.getInterfaces();
308         for (Iterator iter = interfaces.iterator(); iter.hasNext(); ) {
309             JavaClass jc = (JavaClass) iter.next();
310             if (classes.add(jc)) {
311                 classesList.add(jc);
312                 HashSet meths = new HashSet();
313                 collectMethods(meths, jc);
314                 tree.put(jc, meths);
315                 traverseInterfaces(classes, classesList, tree, jc);
316             }
317         }
318     }
319     
320     // ---- TaskListener implementation
321

322     public void taskFinished(Task task) {
323         pcs.firePropertyChange(PROP_FINISHED, Boolean.FALSE, Boolean.TRUE);
324     }
325     
326     // ---- private implementation
327

328     private boolean isAbstract(Method mte) {
329         ClassDefinition cd = mte.getDeclaringClass();
330         return Modifier.isAbstract(mte.getModifiers()) || (cd instanceof JavaClass && ((JavaClass)cd).isInterface());
331     }
332         
333     /** Determines, if the given method is accessible from the root class. */
334     private boolean isAccessibleMethod(Method m) {
335         ClassDefinition cls = m.getDeclaringClass();
336         int modifs = cls instanceof JavaClass ? ((JavaClass)cls).getModifiers() : 0;
337         
338         if ((modifs & Modifier.PRIVATE) != 0 ||
339             ((modifs & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) == 0 &&
340              !isSamePackage(cls))) {
341             return false;
342         }
343         if ((cls instanceof JavaClass) && ((JavaClass)cls).isInterface())
344             return true;
345         modifs = m.getModifiers();
346         if ((modifs & Modifier.PRIVATE) != 0) {
347             return false;
348         }
349         if ((modifs & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0)
350             return true;
351         return isSamePackage(cls);
352     }
353
354     /** Determines if the method is implemented in some super class of the
355      * passed class.
356      */

357     private boolean isImplementedInSuper(ClassDefinition rootCl, Method m, boolean includeClass) {
358         String JavaDoc name = m.getName();
359         List params = m.getParameters();
360         List paramTypes = new ArrayList(params.size());
361         for (Iterator iter = params.iterator(); iter.hasNext(); ) {
362             paramTypes.add(((Parameter)iter.next()).getType());
363         }
364         
365         // traverse all super classes
366
ClassDefinition jc = includeClass ? rootCl : rootCl.getSuperClass();
367         HashSet visited = new HashSet();
368         while (jc != null && visited.add(jc)) {
369             Method mte = jc.getMethod(name, paramTypes, false);
370             if (mte != null && !Modifier.isAbstract(mte.getModifiers())) {
371                 return true;
372             }
373             jc = jc.getSuperClass();
374         }
375         return false;
376     }
377     
378     private boolean isImplemented(Method m) {
379         String JavaDoc name = m.getName();
380         List params = m.getParameters();
381         List paramTypes = new ArrayList(params.size());
382         for (Iterator iter = params.iterator(); iter.hasNext(); ) {
383             paramTypes.add(((Parameter)iter.next()).getType());
384         }
385         
386         // traverse all super classes
387
ClassDefinition jc = root;
388         ClassDefinition realJc = jc instanceof ParameterizedType ? ((ParameterizedType) jc).getDefinition() : jc;
389         HashSet visited = new HashSet();
390         visited.add(m.getDeclaringClass());
391         // String dcName = m.getDeclaringClass().getName();
392
while (jc != null && visited.add(realJc)) {
393             Method mte = jc.getMethod(name, paramTypes, false);
394             if (mte != null && !Modifier.isAbstract(mte.getModifiers())) {
395                 return true;
396             }
397             jc = jc.getSuperClass();
398             realJc = jc instanceof ParameterizedType ? ((ParameterizedType) jc).getDefinition() : jc;
399         }
400         return false;
401     }
402     
403     /**
404      * Determines if the given method is overriden in the hierarchy.
405      *
406      * @param method method for which overriding methods are looked for
407      * @return true, if given method is overriden in subclasses
408      */

409     private boolean isOverriden(Method m) {
410         List params = m.getParameters();
411         String JavaDoc name = m.getName();
412         List paramTypes = new ArrayList(params.size());
413         for (Iterator iter = params.iterator(); iter.hasNext(); ) {
414             paramTypes.add(((Parameter)iter.next()).getType());
415         }
416         ClassDefinition cd = root;
417         HashSet visited = new HashSet();
418         while (cd != null && visited.add(cd)) {
419             Method meth = root.getMethod(name, paramTypes, false);
420             if (meth == m)
421                 return false;
422             else if (meth != null)
423                 return true;
424             cd = cd.getSuperClass();
425         }
426         return false;
427     }
428     
429     /** Deteremines if the class is in the same package as the root class */
430     private boolean isSamePackage(ClassDefinition cd) {
431         String JavaDoc p1 = root.getResource().getPackageName();
432         String JavaDoc p2 = cd.getResource().getPackageName();
433         return p1 == null ? p2 == null : p1.equals(p2);
434     }
435
436     /** Collects all methods declared by the class and all its interfaces. It doesn't
437      * traverse super classes.
438      */

439     private void collectMethods(Set meths, JavaClass jc) {
440         for (Iterator iter = jc.getContents().iterator(); iter.hasNext(); ) {
441             Object JavaDoc obj = iter.next();
442             if (!(obj instanceof Method))
443                 continue;
444             Method m = (Method)obj;
445             int mods = m.getModifiers();
446             boolean modFlag = !Modifier.isStatic(mods) && (Modifier.isPublic(mods) || Modifier.isProtected(mods));
447             boolean isFinal = Modifier.isFinal(mods);
448             if (modFlag && !isFinal && isAccessibleMethod(m) && !isOverriden(m)) {
449                 MethodKey key = new MethodKey(m, ordNumCounter++);
450                 if (!finalMethodsKeys.contains(key) && methods.get(key) == null) {
451                     methods.put(key, m);
452                     meths.add(key);
453                 }
454             } else {
455                 if (isFinal && modFlag) {
456                     MethodKey key = new MethodKey(m, ordNumCounter++);
457                     finalMethodsKeys.add(key);
458                 }
459             }
460         }
461         
462         for (Iterator iter = jc.getInterfaces().iterator(); iter.hasNext(); ) {
463             collectMethods(meths, (JavaClass) iter.next());
464         }
465     }
466
467     private String JavaDoc createSuperCall(Method target, boolean hasSuper) {
468         JavaSynchronizationSettings syncSettings = (JavaSynchronizationSettings)SharedClassObject.findObject(JavaSynchronizationSettings.class, true);
469
470         String JavaDoc body;
471         
472         if (hasSuper) {
473             // create a parameter list for the superclass' call:
474
String JavaDoc format;
475             
476             StringBuffer JavaDoc str = new StringBuffer JavaDoc();
477             for (Iterator iter = target.getParameters().iterator(); iter.hasNext(); ) {
478                 Parameter par = (Parameter) iter.next();
479                 if (str.length() > 0)
480                     str.append(", "); // NOI18N
481
str.append(par.getName());
482             }
483             Type t = target.getType();
484             if (t instanceof PrimitiveType && PrimitiveTypeKindEnum.VOID.equals(((PrimitiveType)t).getKind())) {
485                 format = NbBundle.getMessage(JMIInheritanceSupport.class, "FMT_CallSuper"); // NOI18N
486
} else {
487                 format = NbBundle.getMessage(JMIInheritanceSupport.class, "FMT_ReturnCallSuper"); // NOI18N
488
}
489             body = java.text.MessageFormat.format(format,
490                 new Object JavaDoc[] {
491                     JavaModelUtil.resolveImportsForType(root, target.getType()).getName(),
492                     target.getName(),
493                     str
494             });
495         } else {
496             // no super, let JavaSyncSettings generate a default return:
497
body = syncSettings.getGenerateReturnAsString(target.getType());
498         }
499         return body;
500     }
501     
502     // ..........................................................................
503

504     public static class MethodKey extends Object JavaDoc {
505         
506         private String JavaDoc name;
507         private String JavaDoc[] paramTypes;
508         private int ordNumber;
509         
510         public MethodKey (Method m) {
511             this(m, 0);
512         }
513         
514         public MethodKey (Method m, int ordNumber) {
515             this.ordNumber = ordNumber;
516             name = m.getName();
517             List params = m.getParameters();
518             paramTypes = new String JavaDoc[params.size()];
519             Iterator iter = params.iterator();
520             for (int x = 0; iter.hasNext(); x++) {
521                 paramTypes[x] = ((Parameter)iter.next()).getType().getName();
522             }
523         }
524
525         public int getOrdNumber() {
526             return ordNumber;
527         }
528         
529         /* Returns true if parameters are the same */
530         public boolean equals (Object JavaDoc obj) {
531             if (!(obj instanceof MethodKey)) return false;
532             return name.equals(((MethodKey)obj).name) && Arrays.equals(paramTypes, ((MethodKey)obj).paramTypes);
533         }
534
535         /* Computes hashcode as exclusive or of first and
536         * last parameter's names
537         * (or only from the first or return some constant
538         * for special cases) */

539         public int hashCode () {
540             int length = paramTypes.length;
541             if (length == 0) return 0;
542             if (length == 1) return paramTypes[0].hashCode();
543             return paramTypes[0].hashCode() ^
544                    paramTypes[length - 1].hashCode();
545         }
546
547     }
548     
549     private static class MethodRecord {
550         
551         public Method method;
552         public int orderNumber;
553         
554         MethodRecord(Method method, int orderNumber) {
555             this.method = method;
556             this.orderNumber = orderNumber;
557         }
558         
559     }
560     
561     private static class MethodComparator implements Comparator {
562         
563         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
564             if (o1 instanceof MethodRecord) {
565                 if (o2 instanceof MethodRecord) {
566                     return ((MethodRecord)o1).orderNumber - ((MethodRecord)o2).orderNumber;
567                 } else {
568                     return 1;
569                 }
570             } else {
571                 if (o2 instanceof MethodRecord) {
572                     return -1;
573                 } else {
574                     return 0;
575                 }
576             }
577         }
578         
579     }
580     
581 }
582
Popular Tags