KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jode > decompiler > ClassAnalyzer


1 /* ClassAnalyzer Copyright (C) 1998-2002 Jochen Hoenicke.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; see the file COPYING.LESSER. If not, write to
15  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16  *
17  * $Id: ClassAnalyzer.java.in,v 4.5.2.3 2002/05/28 17:34:03 hoenicke Exp $
18  */

19
20 package jode.decompiler;
21 import jode.GlobalOptions;
22 import jode.type.MethodType;
23 import jode.type.Type;
24 import jode.bytecode.ClassInfo;
25 import jode.bytecode.FieldInfo;
26 import jode.bytecode.MethodInfo;
27 import jode.bytecode.InnerClassInfo;
28 import jode.bytecode.ConstantPool;
29 import jode.expr.Expression;
30 import jode.expr.ThisOperator;
31 import jode.flow.TransformConstructors;
32 import jode.flow.StructuredBlock;
33 import jode.util.SimpleSet;
34
35 import java.lang.reflect.Modifier JavaDoc;
36 import java.util.NoSuchElementException JavaDoc;
37 import java.util.Vector JavaDoc;
38 import java.util.Enumeration JavaDoc;
39 import java.io.IOException JavaDoc;
40
41 import java.util.Collection JavaDoc;
42 import java.util.Set JavaDoc;
43
44 public class ClassAnalyzer
45     implements Scope, Declarable, ClassDeclarer
46 {
47     ImportHandler imports;
48     ClassInfo clazz;
49     ClassDeclarer parent;
50     ProgressListener progressListener;
51
52     /**
53      * The complexity for initi#alizing a class.
54      */

55     private static double INITIALIZE_COMPLEXITY = 0.03;
56     /**
57      * The minimal visible complexity.
58      */

59     private static double STEP_COMPLEXITY = 0.03;
60     /**
61      * The value of the strictfp modifier.
62      * JDK1.1 doesn't define it.
63      */

64     private static int STRICTFP = 0x800;
65
66     double methodComplexity = 0.0;
67     double innerComplexity = 0.0;
68
69     String JavaDoc name;
70     StructuredBlock[] blockInitializers;
71     FieldAnalyzer[] fields;
72     MethodAnalyzer[] methods;
73     ClassAnalyzer[] inners;
74     int modifiers;
75
76     TransformConstructors constrAna;
77     MethodAnalyzer staticConstructor;
78     MethodAnalyzer[] constructors;
79
80     OuterValues outerValues;
81
82     public ClassAnalyzer(ClassDeclarer parent,
83              ClassInfo clazz, ImportHandler imports,
84              Expression[] outerValues)
85     {
86         clazz.loadInfo(clazz.MOSTINFO);
87         this.parent = parent;
88         this.clazz = clazz;
89         this.imports = imports;
90     if (outerValues != null)
91         this.outerValues = new OuterValues(this, outerValues);
92     modifiers = clazz.getModifiers();
93
94     if (parent != null) {
95         InnerClassInfo[] outerInfos = clazz.getOuterClasses();
96         if (outerInfos[0].outer == null || outerInfos[0].name == null) {
97         if (parent instanceof ClassAnalyzer)
98             throw new jode.AssertError
99             ("ClassInfo Attributes are inconsistent: "
100              + clazz.getName());
101         } else {
102         if (!(parent instanceof ClassAnalyzer)
103             || !(((ClassAnalyzer) parent).clazz.getName()
104              .equals(outerInfos[0].outer))
105             || outerInfos[0].name == null)
106             throw new jode.AssertError
107             ("ClassInfo Attributes are inconsistent: "
108              + clazz.getName());
109         }
110         name = outerInfos[0].name;
111         modifiers = outerInfos[0].modifiers;
112     } else {
113         name = clazz.getName();
114         int dot = name.lastIndexOf('.');
115         if (dot >= 0)
116         name = name.substring(dot+1);
117     }
118     }
119
120     public ClassAnalyzer(ClassDeclarer parent,
121              ClassInfo clazz, ImportHandler imports)
122     {
123     this(parent, clazz, imports, null);
124     }
125
126     public ClassAnalyzer(ClassInfo clazz, ImportHandler imports)
127     {
128     this(null, clazz, imports);
129     }
130
131     public final boolean isStatic() {
132         return Modifier.isStatic(modifiers);
133     }
134
135     public final boolean isStrictFP() {
136         return (modifiers & STRICTFP) != 0;
137     }
138
139     public FieldAnalyzer getField(int index) {
140     return fields[index];
141     }
142
143     public int getFieldIndex(String JavaDoc fieldName, Type fieldType) {
144         for (int i=0; i< fields.length; i++) {
145         if (fields[i].getName().equals(fieldName)
146         && fields[i].getType().equals(fieldType))
147         return i;
148         }
149     return -1;
150     }
151     
152     public MethodAnalyzer getMethod(String JavaDoc methodName, MethodType methodType) {
153         for (int i=0; i< methods.length; i++) {
154         if (methods[i].getName().equals(methodName)
155         && methods[i].getType().equals(methodType))
156         return methods[i];
157         }
158     return null;
159     }
160
161     public int getModifiers() {
162     return modifiers;
163     }
164     
165     public ClassDeclarer getParent() {
166         return parent;
167     }
168
169     public void setParent(ClassDeclarer newParent) {
170     this.parent = newParent;
171     }
172
173     public ClassInfo getClazz() {
174         return clazz;
175     }
176
177
178     public String JavaDoc getName() {
179     return name;
180     }
181
182     public void setName(String JavaDoc name) {
183     this.name = name;
184     }
185
186     public OuterValues getOuterValues() {
187     return outerValues;
188     }
189
190     public void addBlockInitializer(int index, StructuredBlock initializer) {
191     if (blockInitializers[index] == null)
192         blockInitializers[index] = initializer;
193     else
194         blockInitializers[index].appendBlock(initializer);
195     }
196
197     public void initialize() {
198         FieldInfo[] finfos = clazz.getFields();
199         MethodInfo[] minfos = clazz.getMethods();
200     InnerClassInfo[] innerInfos = clazz.getInnerClasses();
201
202         if (finfos == null) {
203             /* This means that the class could not be loaded.
204              * give up.
205              */

206             return;
207         }
208
209     if ((Options.options & Options.OPTION_INNER) != 0
210         && innerInfos != null) {
211         /* Create inner classes */
212         Expression[] outerThis = new Expression[] {
213         new ThisOperator(clazz)
214         };
215
216         int innerCount = innerInfos.length;
217         inners = new ClassAnalyzer[innerCount];
218         for (int i=0; i < innerCount; i++) {
219         ClassInfo ci = ClassInfo.forName(innerInfos[i].inner);
220         inners[i] = new ClassAnalyzer
221             (this, ci, imports,
222              Modifier.isStatic(innerInfos[i].modifiers)
223              ? null : outerThis);
224         }
225     } else
226         inners = new ClassAnalyzer[0];
227
228     fields = new FieldAnalyzer[finfos.length];
229     methods = new MethodAnalyzer[minfos.length];
230     blockInitializers = new StructuredBlock[finfos.length+1];
231         for (int j=0; j < finfos.length; j++)
232             fields[j] = new FieldAnalyzer(this, finfos[j], imports);
233
234         staticConstructor = null;
235         Vector JavaDoc constrVector = new Vector JavaDoc();
236         for (int j=0; j < methods.length; j++) {
237             methods[j] = new MethodAnalyzer(this, minfos[j], imports);
238
239             if (methods[j].isConstructor()) {
240                 if (methods[j].isStatic())
241                     staticConstructor = methods[j];
242                 else
243                     constrVector.addElement(methods[j]);
244
245         /* Java bytecode can't have strictfp modifier for
246          * classes, while java can't have strictfp modifier
247          * for constructors. We handle the difference here.
248          *
249          * If only a few constructors are strictfp and the
250          * methods aren't this would add too much strictfp,
251          * but that isn't really dangerous.
252          */

253         if (methods[j].isStrictFP())
254             modifiers |= STRICTFP;
255             }
256         methodComplexity += methods[j].getComplexity();
257         }
258
259         constructors = new MethodAnalyzer[constrVector.size()];
260     constrVector.copyInto(constructors);
261
262     // initialize the inner classes.
263
for (int j=0; j < inners.length; j++) {
264         inners[j].initialize();
265         innerComplexity += inners[j].getComplexity();
266     }
267     }
268
269     /**
270      * Gets the complexity of this class. Must be called after it has
271      * been initialized. This is used for a nice progress bar.
272      */

273     public double getComplexity() {
274     return (methodComplexity + innerComplexity);
275     }
276
277     public void analyze(ProgressListener pl, double done, double scale) {
278     if (GlobalOptions.verboseLevel > 0)
279         GlobalOptions.err.println("Class " + name);
280     double subScale = scale / methodComplexity;
281     if (pl != null)
282         pl.updateProgress(done, name);
283
284     imports.useClass(clazz);
285         if (clazz.getSuperclass() != null)
286             imports.useClass(clazz.getSuperclass());
287         ClassInfo[] interfaces = clazz.getInterfaces();
288         for (int j=0; j< interfaces.length; j++)
289             imports.useClass(interfaces[j]);
290
291         if (fields == null) {
292             /* This means that the class could not be loaded.
293              * give up.
294              */

295             return;
296         }
297
298
299     // First analyze constructors and synthetic fields:
300
constrAna = null;
301     if (constructors.length > 0) {
302         for (int j=0; j< constructors.length; j++)
303         {
304             if (pl != null) {
305             double constrCompl = constructors[j].getComplexity()
306                 * subScale;
307             if (constrCompl > STEP_COMPLEXITY)
308                 constructors[j].analyze(pl, done, constrCompl);
309             else {
310                 pl.updateProgress(done, name);
311                 constructors[j].analyze(null, 0.0, 0.0);
312             }
313             done += constrCompl;
314             } else
315             constructors[j].analyze(null, 0.0, 0.0);
316         }
317         constrAna = new TransformConstructors(this, false, constructors);
318         constrAna.removeSynthInitializers();
319         }
320     if (staticConstructor != null) {
321         if (pl != null) {
322         double constrCompl
323             = staticConstructor.getComplexity() * subScale;
324         if (constrCompl > STEP_COMPLEXITY)
325             staticConstructor.analyze(pl, done, constrCompl);
326         else {
327             pl.updateProgress(done, name);
328             staticConstructor.analyze(null, 0.0, 0.0);
329         }
330         done += constrCompl;
331         } else
332         staticConstructor.analyze(null, 0.0, 0.0);
333     }
334
335     // If output should be immediate, we delay analyzation to output.
336
// Note that this may break anonymous classes, but the user
337
// has been warned.
338
if ((Options.options & Options.OPTION_IMMEDIATE) != 0)
339         return;
340
341     // Analyze fields
342
for (int j=0; j < fields.length; j++)
343         fields[j].analyze();
344
345     // Now analyze remaining methods.
346
for (int j=0; j < methods.length; j++) {
347         if (!methods[j].isConstructor())
348         if (pl != null) {
349             double methodCompl = methods[j].getComplexity()
350             * subScale;
351             if (methodCompl > STEP_COMPLEXITY)
352             methods[j].analyze(pl, done, methodCompl);
353             else {
354             pl.updateProgress(done, methods[j].getName());
355             methods[j].analyze(null, 0.0, 0.0);
356             }
357             done += methodCompl;
358         } else
359             methods[j].analyze(null, 0.0, 0.0);
360     }
361     }
362
363     public void analyzeInnerClasses(ProgressListener pl,
364                     double done, double scale) {
365     double subScale = scale / innerComplexity;
366     // If output should be immediate, we delay analyzation to output.
367
// Note that this may break anonymous classes, but the user
368
// has been warned.
369
if ((Options.options & Options.OPTION_IMMEDIATE) != 0)
370         return;
371
372     // Now analyze the inner classes.
373
for (int j=0; j < inners.length; j++) {
374         if (pl != null) {
375         double innerCompl = inners[j].getComplexity() * subScale;
376         if (innerCompl > STEP_COMPLEXITY) {
377             double innerscale = subScale * inners[j].methodComplexity;
378             inners[j].analyze(pl, done, innerscale);
379             inners[j].analyzeInnerClasses(null, done + innerscale,
380                           innerCompl - innerscale);
381         } else {
382             pl.updateProgress(done, inners[j].name);
383             inners[j].analyze(null, 0.0, 0.0);
384             inners[j].analyzeInnerClasses(null, 0.0, 0.0);
385         }
386         done += innerCompl;
387         } else {
388         inners[j].analyze(null, 0.0, 0.0);
389         inners[j].analyzeInnerClasses(null, 0.0, 0.0);
390         }
391     }
392
393     // Now analyze the method scoped classes.
394
for (int j=0; j < methods.length; j++)
395         methods[j].analyzeInnerClasses();
396
397     }
398
399     public void makeDeclaration(Set done) {
400     if (constrAna != null)
401         constrAna.transform();
402         if (staticConstructor != null) {
403             new TransformConstructors
404         (this, true, new MethodAnalyzer[] { staticConstructor })
405         .transform();
406     }
407
408     // If output should be immediate, we delay analyzation to output.
409
// Note that this may break anonymous classes, but the user
410
// has been warned.
411
if ((Options.options & Options.OPTION_IMMEDIATE) != 0)
412         return;
413
414         for (int j=0; j < fields.length; j++)
415         fields[j].makeDeclaration(done);
416         for (int j=0; j < inners.length; j++)
417         inners[j].makeDeclaration(done);
418         for (int j=0; j < methods.length; j++)
419         methods[j].makeDeclaration(done);
420     }
421
422     public void dumpDeclaration(TabbedPrintWriter writer) throws IOException JavaDoc
423     {
424     dumpDeclaration(writer, null, 0.0, 0.0);
425     }
426
427     public void dumpDeclaration(TabbedPrintWriter writer,
428                 ProgressListener pl, double done, double scale)
429     throws IOException JavaDoc
430     {
431         if (fields == null) {
432             /* This means that the class could not be loaded.
433              * give up.
434              */

435             return;
436         }
437
438     writer.startOp(writer.NO_PAREN, 0);
439     /* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */
440     int modifiedModifiers = modifiers & ~(Modifier.SYNCHRONIZED
441                           | STRICTFP);
442     if (clazz.isInterface())
443         /* interfaces are implicitily abstract */
444         modifiedModifiers &= ~Modifier.ABSTRACT;
445     if (parent instanceof MethodAnalyzer) {
446         /* method scope classes are implicitly private */
447         modifiedModifiers &= ~Modifier.PRIVATE;
448         /* anonymous classes are implicitly final */
449         if (name == null)
450         modifiedModifiers &= ~Modifier.FINAL;
451     }
452         String JavaDoc modif = Modifier.toString(modifiedModifiers);
453         if (modif.length() > 0)
454             writer.print(modif + " ");
455     if (isStrictFP()) {
456         /* The STRICTFP modifier is set.
457          * We handle it, since java.lang.reflect.Modifier is too dumb.
458          */

459         writer.print("strictfp ");
460     }
461     /* interface is in modif */
462     if (!clazz.isInterface())
463         writer.print("class ");
464     writer.print(name);
465     ClassInfo superClazz = clazz.getSuperclass();
466     if (superClazz != null &&
467         superClazz != ClassInfo.javaLangObject) {
468         writer.breakOp();
469         writer.print(" extends " + (writer.getClassString
470                        (superClazz, Scope.CLASSNAME)));
471     }
472     ClassInfo[] interfaces = clazz.getInterfaces();
473     if (interfaces.length > 0) {
474         writer.breakOp();
475         writer.print(clazz.isInterface() ? " extends " : " implements ");
476         writer.startOp(writer.EXPL_PAREN, 1);
477         for (int i=0; i < interfaces.length; i++) {
478         if (i > 0) {
479             writer.print(", ");
480             writer.breakOp();
481         }
482         writer.print(writer.getClassString
483                  (interfaces[i], Scope.CLASSNAME));
484         }
485         writer.endOp();
486     }
487     writer.println();
488
489     writer.openBraceClass();
490     writer.tab();
491     dumpBlock(writer, pl, done, scale);
492     writer.untab();
493     writer.closeBraceClass();
494     }
495     
496     public void dumpBlock(TabbedPrintWriter writer)
497     throws IOException JavaDoc
498     {
499     dumpBlock(writer, null, 0.0, 0.0);
500     }
501
502     public void dumpBlock(TabbedPrintWriter writer,
503               ProgressListener pl, double done, double scale)
504     throws IOException JavaDoc
505     {
506     double subScale = scale / getComplexity();
507     writer.pushScope(this);
508     boolean needFieldNewLine = false;
509     boolean needNewLine = false;
510     Set declared = null;
511     if ((Options.options & Options.OPTION_IMMEDIATE) != 0)
512         declared = new SimpleSet();
513     for (int i=0; i< fields.length; i++) {
514         if (blockInitializers[i] != null) {
515         if (needNewLine)
516             writer.println();
517         writer.openBrace();
518         writer.tab();
519         blockInitializers[i].dumpSource(writer);
520         writer.untab();
521         writer.closeBrace();
522         needFieldNewLine = needNewLine = true;
523         }
524         if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
525         // We now do the analyzation we skipped before.
526
fields[i].analyze();
527         fields[i].makeDeclaration(declared);
528         }
529         if (fields[i].skipWriting())
530         continue;
531         if (needFieldNewLine)
532         writer.println();
533         fields[i].dumpSource(writer);
534         needNewLine = true;
535     }
536     if (blockInitializers[fields.length] != null) {
537         if (needNewLine)
538         writer.println();
539         writer.openBrace();
540         writer.tab();
541         blockInitializers[fields.length].dumpSource(writer);
542         writer.untab();
543         writer.closeBrace();
544         needNewLine = true;
545     }
546     for (int i=0; i< inners.length; i++) {
547         if (needNewLine)
548         writer.println();
549
550         if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
551         // We now do the analyzation we skipped before.
552
inners[i].analyze(null, 0.0, 0.0);
553         inners[i].analyzeInnerClasses(null, 0.0, 0.0);
554         inners[i].makeDeclaration(declared);
555         }
556
557         if (pl != null) {
558         double innerCompl = inners[i].getComplexity() * subScale;
559         if (innerCompl > STEP_COMPLEXITY)
560             inners[i].dumpSource(writer, pl, done, innerCompl);
561         else {
562             pl.updateProgress(done, name);
563             inners[i].dumpSource(writer);
564         }
565         done += innerCompl;
566         } else
567         inners[i].dumpSource(writer);
568         needNewLine = true;
569     }
570     for (int i=0; i< methods.length; i++) {
571         if ((Options.options & Options.OPTION_IMMEDIATE) != 0) {
572         // We now do the analyzation we skipped before.
573
if (!methods[i].isConstructor())
574             methods[i].analyze(null, 0.0, 0.0);
575         methods[i].analyzeInnerClasses();
576         methods[i].makeDeclaration(declared);
577         }
578
579         if (methods[i].skipWriting())
580         continue;
581         if (needNewLine)
582         writer.println();
583
584         if (pl != null) {
585         double methodCompl = methods[i].getComplexity() * subScale;
586         pl.updateProgress(done, methods[i].getName());
587         methods[i].dumpSource(writer);
588         done += methodCompl;
589         } else
590         methods[i].dumpSource(writer);
591         needNewLine = true;
592     }
593     writer.popScope();
594         clazz.dropInfo(clazz.KNOWNATTRIBS | clazz.UNKNOWNATTRIBS);
595     }
596
597     public void dumpSource(TabbedPrintWriter writer)
598     throws IOException JavaDoc
599     {
600     dumpSource(writer, null, 0.0, 0.0);
601     }
602
603     public void dumpSource(TabbedPrintWriter writer,
604                ProgressListener pl, double done, double scale)
605     throws IOException JavaDoc
606     {
607     dumpDeclaration(writer, pl, done, scale);
608     writer.println();
609     }
610
611     public void dumpJavaFile(TabbedPrintWriter writer)
612     throws IOException JavaDoc {
613     dumpJavaFile(writer, null);
614     }
615
616     public void dumpJavaFile(TabbedPrintWriter writer, ProgressListener pl)
617     throws IOException JavaDoc {
618     imports.init(clazz.getName());
619     LocalInfo.init();
620     initialize();
621     double done = 0.05;
622     double scale = (0.75) * methodComplexity
623         / (methodComplexity + innerComplexity);
624     analyze(pl, INITIALIZE_COMPLEXITY, scale);
625     done += scale;
626     analyzeInnerClasses(pl, done, 0.8 - done);
627     makeDeclaration(new SimpleSet());
628     imports.dumpHeader(writer);
629     dumpSource(writer, pl, 0.8, 0.2);
630     if (pl != null)
631         pl.updateProgress(1.0, name);
632     }
633
634     public boolean isScopeOf(Object JavaDoc obj, int scopeType) {
635     if (clazz.equals(obj) && scopeType == CLASSSCOPE)
636         return true;
637     return false;
638     }
639
640     static int serialnr = 0;
641     public void makeNameUnique() {
642     name = name + "_" + serialnr++ + "_";
643     }
644
645     public boolean conflicts(String JavaDoc name, int usageType) {
646     return conflicts(clazz, name, usageType);
647     }
648
649     private static boolean conflicts(ClassInfo info,
650                      String JavaDoc name, int usageType) {
651     while (info != null) {
652         if (usageType == NOSUPERMETHODNAME || usageType == METHODNAME) {
653         MethodInfo[] minfos = info.getMethods();
654         for (int i = 0; i< minfos.length; i++)
655             if (minfos[i].getName().equals(name))
656             return true;
657         }
658         if (usageType == NOSUPERFIELDNAME || usageType == FIELDNAME
659         || usageType == AMBIGUOUSNAME) {
660         FieldInfo[] finfos = info.getFields();
661         for (int i=0; i < finfos.length; i++) {
662             if (finfos[i].getName().equals(name))
663             return true;
664         }
665         }
666         if (usageType == CLASSNAME || usageType == AMBIGUOUSNAME) {
667         InnerClassInfo[] iinfos = info.getInnerClasses();
668         if (iinfos != null) {
669             for (int i=0; i < iinfos.length; i++) {
670             if (iinfos[i].name.equals(name))
671                 return true;
672             }
673         }
674         }
675         if (usageType == NOSUPERFIELDNAME
676         || usageType == NOSUPERMETHODNAME)
677         return false;
678
679         ClassInfo[] ifaces = info.getInterfaces();
680         for (int i = 0; i < ifaces.length; i++)
681         if (conflicts(ifaces[i], name, usageType))
682             return true;
683         info = info.getSuperclass();
684     }
685     return false;
686     }
687
688     /**
689      * Get the class analyzer for the given class info. This searches
690      * the method scoped/anonymous classes in this method and all
691      * outer methods and the outer classes for the class analyzer.
692      * @param cinfo the classinfo for which the analyzer is searched.
693      * @return the class analyzer, or null if there is not an outer
694      * class that equals cinfo, and not a method scope/inner class in
695      * an outer method.
696      */

697     public ClassAnalyzer getClassAnalyzer(ClassInfo cinfo) {
698     if (cinfo == getClazz())
699         return this;
700     if (parent == null)
701         return null;
702     return getParent().getClassAnalyzer(cinfo);
703     }
704
705     /**
706      * Get the class analyzer for the given inner class.
707      * @param name the short name of the inner class
708      * @return the class analyzer, or null if there is no inner
709      * class with the given name.
710      */

711     public ClassAnalyzer getInnerClassAnalyzer(String JavaDoc name) {
712     /** require name != null; **/
713     int innerCount = inners.length;
714     for (int i=0; i < innerCount; i++) {
715         if (inners[i].name.equals(name))
716         return inners[i];
717     }
718     return null;
719     }
720
721     /**
722      * We add the named method scoped classes to the declarables.
723      */

724     public void fillDeclarables(Collection JavaDoc used) {
725         for (int j=0; j < methods.length; j++)
726         methods[j].fillDeclarables(used);
727     }
728
729     public void addClassAnalyzer(ClassAnalyzer clazzAna) {
730     if (parent != null)
731         parent.addClassAnalyzer(clazzAna);
732     }
733
734     public String JavaDoc toString() {
735     return getClass().getName()+"["+getClazz()+"]";
736     }
737 }
738
Popular Tags