Source code: com/puppycrawl/tools/checkstyle/checks/usage/transmogrify/TableMaker.java
1
2 // Transmogrify License
3 //
4 // Copyright (c) 2001, ThoughtWorks, Inc.
5 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 // - Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // - Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // Neither the name of the ThoughtWorks, Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from this
16 // software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
30
31 import java.io.File;
32 import java.util.Enumeration;
33 import java.util.Vector;
34
35 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
36
37
38
39
40 /**
41 * this is the class that does the work of the "walking" step --
42 * going through the SymTabAST and constructing the definitions and
43 * references. The SymTabAST is constructed in a DOMish fashion, i.e.
44 * each node knows about its first child and its next sibling. The
45 * DFS search is carried out by two functions -- walkTree, which
46 * deals with a single SymTabAST node, and walkSibilngs, which deals with
47 * all the siblings of an SymTabAST node.
48 */
49
50 public class TableMaker {
51 private SymbolTable symbolTable;
52 private SymTabAST _tree;
53 private File currentFile;
54 private Vector imports = new Vector();
55
56 /**
57 * Constructor for the TableMaker class
58 *
59 * @param tree is the<code>SymTabAST</code> from which to
60 * build the <code>TableMaker</code>. It is the tree which wil be walked
61 * in order to build definitions and references.
62 */
63 public TableMaker(SymTabAST tree)
64 {
65 _tree = tree;
66 }
67
68 /**
69 * returns the <code> SymTabAST </code> which is the
70 * SymTabAST generated from the parsed Java Source files.
71 *
72 * @return <code>SymTabAST</code>
73 */
74 public SymTabAST getTree() {
75 return _tree;
76 }
77
78 /**
79 * returns the <code>SymbolTable</code> that has been constructed by
80 * this <code>TableMaker</code>
81 *
82 * @return <code>SymbolTable</code>
83 * @throws <code>SymbolTableException</code>
84 */
85 public SymbolTable getTable() throws SymbolTableException {
86 if (symbolTable == null) {
87 symbolTable = new SymbolTable( _tree );
88
89 createDefinitions();
90
91 resolveReferences();
92 }
93
94 return symbolTable;
95 }
96
97 /**
98 * walks the tree and finishes creating definitions.
99 *
100 * @return <code>void</code>
101 * @throws <code>SymbolTableException</code>
102 * @see #walkTree()
103 * @see #finishCreatingDefinitions()
104 */
105 private void createDefinitions() throws SymbolTableException {
106 walkTree();
107 finishCreatingDefinitions();
108 }
109
110 /**
111 * finishes up creating definitions process
112 * starts at the base of the Table
113 *
114 * @return <code>void</code>
115 * @throws <code>SymbolTableException</code>
116 * @see net.sourceforge.transmogrify.symtab.SymbolTable
117 * @see #finishCreatingDefinition(Definition)
118 */
119 private void finishCreatingDefinitions() throws SymbolTableException {
120 finishCreatingDefinition(symbolTable.getBaseScope());
121 }
122
123 /**
124 * begins at the base of the Table and starts finishing definition creation.
125 *
126 * @param def <code>Definition</code> needs to be completed
127 * @return <code>void</code>
128 * @throws <code>SymbolTableException</code>
129 * @see ClassFinisher
130 * @see VariableFinisher
131 * @see MethodFinisher
132 * @see CatchFinisher
133 */
134 private void finishCreatingDefinition(Definition def) throws SymbolTableException {
135
136 if (def instanceof AnonymousInnerClass) {
137 AnonymousInnerClass innerClass = (AnonymousInnerClass)def;
138 innerClass.finishMakingDefinition();
139 }
140 else if (def instanceof ClassDef) {
141 new ClassFinisher((ClassDef)def).finish();
142 }
143 else if ( def instanceof VariableDef ) {
144 new VariableFinisher( (VariableDef)def ).finish();
145 }
146 else if (def instanceof DefaultConstructor) {}
147 else if ( def instanceof MethodDef ) {
148 new MethodFinisher( (MethodDef)def ).finish();
149 }
150 else if (def instanceof BlockDef) {
151 SymTabAST firstChild = (SymTabAST)def.getTreeNode().getFirstChild();
152 //handle Checkstyle grammar
153 if (firstChild.getType() == TokenTypes.LPAREN) {
154 firstChild = (SymTabAST) firstChild.getNextSibling();
155 }
156 if (firstChild.getType() == TokenTypes.PARAMETER_DEF) {
157 // this is a catch block
158 new CatchFinisher((BlockDef)def).finish();
159 }
160 }
161
162 if ( def instanceof Scope ) {
163 finishCreatingChildren((Scope)def);
164 }
165 }
166
167
168 /**
169 * iterates through all the definitions in the <code> Scope </code>
170 * finishes creating each <code>Scope</code>.
171 *
172 * @param scope <code> Scope </code> where defininitions will be finished.
173 * @return <code>void</code>
174 * @throws <code>SymbolTableException</code>
175 * @see net.sourceforge.transmogrify.symtab.Scope
176 * @see #finishCreatingDefinition(Definition)
177 */
178 private void finishCreatingChildren( Scope scope ) throws SymbolTableException {
179 Enumeration definitions = scope.getDefinitions();
180 while ( definitions.hasMoreElements() ) {
181 Definition child = (Definition)(definitions.nextElement());
182 finishCreatingDefinition(child);
183 }
184 }
185
186 /**
187 * resolves <code>SymbolTable</code> using <code>Resolver</code>
188 *
189 * @return <code>void</code>
190 * @see net.sourceforge.transmogrify.symtab.Resolver
191 * @see net.sourceforge.transmogrify.symtab.SymbolTable
192 */
193 private void resolveReferences() {
194 new Resolver( symbolTable ).resolve();
195 }
196
197 /**
198 * starts walking the <code> SymTabAST </code>
199 *
200 * @return <code>void</code>
201 * @see #walkSiblings(SymTabAST,boolean)
202 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
203 */
204 public void walkTree() {
205 walkSiblings((SymTabAST)_tree.getFirstChild(), false);
206 }
207
208 /**
209 * processes a single SymTabAST node based on its type and passes of its
210 * children for further processing where appropriate.
211 *
212 * @param tree the <code>SymTabAST</code> node to process
213 * @param makeAnonymousScopes sets to <code>false</code> after walking Class,
214 * Methods and Try otherwise sets to <code>true</code>
215 * @return <code>void</code>
216 * @see #walkSiblings(SymTabAST, boolean)
217 * @see #processAnonymousInnerClass(SymTabAST, SymTabAST)
218 * @see #processBlock(SymTabAST, boolean)
219 * @see #processClass(SymTabAST)
220 * @see #processConstructorDef(SymTabAST)
221 * @see #processElse(SymTabAST)
222 * @see #processFile(SymTabAST)
223 * @see #processFinally(SymTabAST)
224 * @see #processFor(SymTabAST)
225 * @see #processImplicitPackage(SymTabAST)
226 * @see #processImport(SymTabAST)
227 * @see #processLabel(SymTabAST)
228 * @see #processMethodDef(SymTabAST)
229 * @see #processPackage(SymTabAST)
230 * @see #processTry(SymTabAST)
231 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
232 * @see net.sourceforge.transmogrify.symtab.antlr.JavaTokenTypes
233 */
234 public void walkTree(SymTabAST tree, boolean makeAnonymousScopes) {
235
236 if (tree != null) {
237
238 ((SymTabAST)tree).setScope( symbolTable.getCurrentScope() );
239
240 switch(tree.getType()) {
241 case 0:
242 processFile(tree);
243 if ( tree.getFirstChild().getType() != TokenTypes.PACKAGE_DEF ) {
244 processImplicitPackage( ((SymTabAST)tree).getFile() );
245 }
246
247 walkSiblings((SymTabAST)tree.getFirstChild(), false);
248
249 // pop package scope
250 symbolTable.popScope();
251 clearImports();
252 break;
253
254 case TokenTypes.LITERAL_NEW:
255 SymTabAST symtabTree = (SymTabAST)tree;
256 SymTabAST objblock
257 = symtabTree.findFirstToken(TokenTypes.OBJBLOCK);
258 if (objblock != null) {
259 SymTabAST classExtended
260 = symtabTree.findFirstToken(TokenTypes.IDENT);
261
262 processAnonymousInnerClass(objblock, classExtended);
263 }
264 break;
265
266 case TokenTypes.SLIST:
267 if (makeAnonymousScopes) {
268 processBlock(tree, true);
269 }
270 else {
271 walkSiblings((SymTabAST)tree.getFirstChild(), true);
272 }
273 break;
274
275 case TokenTypes.CTOR_DEF:
276 processConstructorDef(tree);
277 break;
278
279 case TokenTypes.METHOD_DEF:
280 processMethodDef(tree);
281 break;
282
283 case TokenTypes.LITERAL_FINALLY:
284 processFinally(tree);
285 break;
286
287 case TokenTypes.LITERAL_TRY:
288 processTry(tree);
289 break;
290
291 case TokenTypes.VARIABLE_DEF:
292 processVariableDef(tree);
293 break;
294
295 case TokenTypes.PACKAGE_DEF:
296 processPackage(tree);
297 break;
298
299 case TokenTypes.LABELED_STAT:
300 processLabel(tree);
301 break;
302
303 case TokenTypes.IMPORT:
304 processImport(tree);
305 break;
306
307 case TokenTypes.CLASS_DEF:
308 case TokenTypes.INTERFACE_DEF:
309 processClass(tree);
310 break;
311
312 case TokenTypes.LITERAL_FOR:
313 processFor(tree);
314 break;
315
316 case TokenTypes.LITERAL_IF:
317 processIf(tree);
318 break;
319
320 case TokenTypes.LITERAL_ASSERT:
321 processAssert(tree);
322 break;
323
324 case TokenTypes.LITERAL_CATCH:
325 case TokenTypes.LITERAL_WHILE:
326 case TokenTypes.LITERAL_SWITCH:
327 case TokenTypes.LITERAL_DO:
328 case TokenTypes.LITERAL_SYNCHRONIZED:
329 case TokenTypes.STATIC_INIT:
330 case TokenTypes.INSTANCE_INIT:
331 processBlock(tree, false);
332 break;
333
334 default:
335 walkSiblings((SymTabAST)tree.getFirstChild(), false);
336 }
337 }
338 }
339
340 /**
341 * @param tree
342 */
343 public void processAssert(SymTabAST tree) {
344 BlockDef block = makeBlock((SymTabAST)tree);
345
346 SymTabAST expr = tree.findFirstToken(TokenTypes.EXPR);
347 SymTabAST message = (SymTabAST)expr.getNextSibling();
348 while ((message != null) && (message.getType() != TokenTypes.EXPR)) {
349 message = (SymTabAST) message.getNextSibling();
350 }
351
352
353 symbolTable.pushScope( block );
354 walkTree(expr, false);
355 if (message != null) {
356 walkTree(message, false);
357 }
358 symbolTable.popScope();
359 }
360
361 /**
362 * processes the given <code>SymTabAST</code> and each of its siblings
363 *
364 * @param tree <code>SymTabAST</code> node to process
365 * @param makeAnonymousScopes
366 *
367 * @return <code>void</code>
368 * @see #walkTree(SymTabAST, boolean)
369 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
370 */
371 public void walkSiblings(SymTabAST tree, boolean makeAnonymousScopes) {
372 while(tree != null) {
373 walkTree(tree, makeAnonymousScopes);
374 tree = (SymTabAST)tree.getNextSibling();
375 }
376 }
377
378 /**
379 * processes the given <code>SymTabAST</code> as a package defintion
380 *
381 * @param tree the <code>SymTabAST</code> to process
382 * @return <code>void</code>
383 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
384 * @see net.sourceforge.transmogrify.symtab.PackageDef
385 * @see net.sourceforge.transmogrify.symtab.SymbolTable
386 */
387 public void processPackage(SymTabAST tree) {
388 SymTabAST firstChild = (SymTabAST)tree.getFirstChild();
389
390 String name = ASTUtil.constructDottedName(firstChild);
391 firstChild.ignoreChildren();
392
393 PackageDef pkg = symbolTable.getPackage(name);
394
395 if (pkg == null) {
396 pkg = createPackage( (SymTabAST)(tree.getFirstChild()) );
397 }
398
399 symbolTable.pushScope(pkg);
400 }
401
402 /**
403 * processes a java class that use default no package
404 *
405 * @param file <code>File</code> object of the java class
406 * @return <code>void</code>
407 * @see net.sourceforge.transmogrify.symtab.PackageDef
408 * @see net.sourceforge.transmogrify.symtab.SymbolTable
409 */
410 public void processImplicitPackage( File file ) {
411 String name = file.getParent();
412 if (name == null) {
413 name = "";
414 }
415 PackageDef pkg = symbolTable.getPackage( name );
416
417 if ( pkg == null ) {
418 pkg = new PackageDef( name, symbolTable.getBaseScope(), null );
419 symbolTable.definePackage( pkg, symbolTable.getBaseScope() );
420 }
421
422 symbolTable.pushScope( pkg );
423 }
424
425 /**
426 * gets the package represented by the tree. The method
427 * analyzes the tree, constructs an appropriate package name
428 * and fetches it from the internal package list. If the package does not
429 * exist it is created.
430 *
431 * @param tree <code>SymTabAST</code> to consider
432 *
433 * @return <code>PackageDef</code> the resulting package definition
434 * @see #getPackage(Scope, SymTabAST)
435 */
436 private PackageDef createPackage( SymTabAST tree ) {
437 PackageDef result = null;
438
439 if (tree.getType() == TokenTypes.DOT) {
440 // find the package result of left child
441 SymTabAST leftChild = (SymTabAST)tree.getFirstChild();
442 SymTabAST rightChild = (SymTabAST)leftChild.getNextSibling();
443
444 PackageDef context = createPackage(leftChild);
445 result = getPackage( context, rightChild );
446 }
447 else {
448 result = getPackage(symbolTable.getBaseScope(), tree);
449 }
450
451 return result;
452 }
453
454 /**
455 * gets the package determined by the tree and parent package def.
456 * The method analyzes the tree and parent scope and retrieves the
457 * appropriate package definition from the internal package list.
458 * If the package does not exist it is created.
459 *
460 * @param tree <code>SymTabAST</code> to consider
461 * @param parent the parent package definition
462 *
463 * @return PackageDef the resulting package definition
464 * @see net.sourceforge.transmogrify.symtab.PackageDef
465 * @see net.sourceforge.transmogrify.symtab.SymbolTable
466 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
467 */
468 private PackageDef getPackage(Scope parent, SymTabAST tree ) {
469 String name = tree.getText();
470 PackageDef result = null;
471 if (!(parent instanceof BaseScope)) {
472 result = symbolTable.getPackage(parent.getQualifiedName() + "." + name);
473 }
474 else {
475 result = symbolTable.getPackage(name);
476 }
477
478 if (result == null) {
479 result = new PackageDef(tree.getText(), parent, tree);
480 symbolTable.definePackage(result, parent);
481 }
482
483 return result;
484 }
485
486 /**
487 * process the given <code>SymTabAST</code> as a file definition
488 *
489 * @param tree the <code>SymTabAST</code> to process
490 * @return <code>void</code>
491 * @see #setCurrentFile(String)
492 */
493 public void processFile(SymTabAST tree) {
494 setCurrentFile(tree.getText());
495 }
496
497 /**
498 * adds the given <code>SymTabAST</code> to <code>imports</code> member
499 *
500 * @param tree the <code>SymTabAST</code> to process
501 * @return <code>void</code>
502 */
503 public void processImport(SymTabAST tree) {
504 imports.add( tree );
505 }
506
507 /**
508 * clears the <code>imports</code> member
509 * @return <code>void</code>
510 */
511 private void clearImports() {
512 imports.clear();
513 }
514
515 /**
516 * process the given SymTabAST as a label definition
517 *
518 * @param tree the SymTabAST to process
519 * @return <code>void</code>
520 * @see com.trwx.symtab.antlr.SymTabAST
521 * @see net.sourceforge.transmogrify.symtab.LabelDef
522 * @see #walkTree(SymTabAST, boolean)
523 */
524 public void processLabel(SymTabAST tree) {
525 String name = tree.findFirstToken(TokenTypes.IDENT).getText();
526 LabelDef label = new LabelDef( name, symbolTable.getCurrentScope(),
527 (SymTabAST)tree );
528 symbolTable.defineLabel( label );
529
530 walkTree((SymTabAST)tree.getFirstChild().getNextSibling(), false);
531 }
532
533 /**
534 * process the given <code>SymTabAST</code> as a class definition
535 *
536 * @param tree the <code>SymTabAST</code> to process
537 * @return <code>void</code>
538 * @see #makeClass(String, SymTabAST)
539 * @see #walkSiblings(SymTabAST, boolean)
540 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
541 */
542 public void processClass(SymTabAST tree) {
543 String name = tree.findFirstToken(TokenTypes.IDENT).getText();
544
545 makeClass(name, tree);
546 final SymTabAST objblock =
547 (SymTabAST)tree.findFirstToken(TokenTypes.OBJBLOCK);
548 SymTabAST start = (SymTabAST)objblock.getFirstChild();
549 if (start != null) {
550 //skip LPAREN
551 if (start.getType() == TokenTypes.LPAREN) {
552 start = (SymTabAST)start.getNextSibling();
553 }
554 walkSiblings(start, false);
555 }
556
557 symbolTable.popScope();
558 }
559
560 /**
561 * creates <code>ClassDef</code> for the current class node and add it to the
562 * symbol table
563 * @param name name of the class
564 * @param tree current node to be processed
565 * @return <code>void</code>
566 * @see net.sourceforge.transmogrify.symtab.ClassDef
567 * @see net.sourceforge.transmogrify.symtab.SymbolTable
568 */
569 public void makeClass(String name, SymTabAST tree) {
570 ClassDef def = new ClassDef(name, symbolTable.getCurrentScope(), tree);
571 def.addUnprocessedImports(imports);
572 symbolTable.defineClass(def);
573 symbolTable.pushScope(def);
574 }
575
576 /**
577 * processes anonymous inner class encountered in the tree
578 * @param objblock the anonymous block
579 * @param classExtended
580 * @return <code>void</code>
581 * @see net.sourceforge.transmogrify.symtab.AnonymousInnerClass
582 * @see net.sourceforge.transmogrify.symtab.SymbolTable
583 */
584 public void processAnonymousInnerClass(SymTabAST objblock,
585 SymTabAST classExtended) {
586 ClassDef def = new AnonymousInnerClass(objblock,
587 classExtended,
588 symbolTable.getCurrentScope());
589 symbolTable.defineClass(def);
590 symbolTable.pushScope(def);
591 walkSiblings((SymTabAST)objblock.getFirstChild(), false);
592 symbolTable.popScope();
593 }
594
595 /**
596 * process the given SymTabAST as a variable definition
597 *
598 * @param tree the SymTabAST to process
599 * @return <code>void</code>
600 * @see net.sourceforge.transmogrify.symtab.VariableDef
601 * @see net.sourceforge.transmogrify.symtab.SymbolTable
602 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
603 * @see #makeVariableDef(SymTabAST, Scope)
604 * @see #walkTree(SymTabAST, boolean)
605 */
606 private void processVariableDef(SymTabAST tree) {
607 VariableDef def = makeVariableDef( tree, symbolTable.getCurrentScope() );
608 symbolTable.defineVariable(def);
609 SymTabAST assignmentNode
610 = ((SymTabAST)tree).findFirstToken(TokenTypes.ASSIGN);
611 if (assignmentNode != null) {
612 walkTree((SymTabAST)assignmentNode.getFirstChild(), false);
613 }
614 }
615
616 /**
617 * creates <code>VariableDef</code> based on the current tree node and scope
618 * @param tree the current tree node
619 * @param scope the current scope
620 * @return <code>VariableDef</code>
621 * @see net.sourceforge.transmogrify.symtab.VariableDef
622 */
623 public VariableDef makeVariableDef(SymTabAST tree, Scope scope) {
624 String name = tree.findFirstToken(TokenTypes.IDENT).getText();
625 VariableDef result = new VariableDef(name, scope, tree);
626
627 return result;
628 }
629
630 /**
631 * process the given SymTabAST as a try block
632 *
633 * @param tree the SymTabAST to process
634 * @return <code>void</code>
635 * @see #makeBlock(SymTabAST)
636 * @see net.sourceforge.transmogrify.symtab.antlr.SymTabAST
637 * @see #walkTree(SymTabAST, boolean)
638 * @see #walkSiblings(SymTabAST, boolean)
639 */
640 public void processTry(SymTabAST tree){
641 BlockDef block = makeBlock(tree);
642
643 SymTabAST slist = tree.findFirstToken(TokenTypes.SLIST);
644 SymTabAST everythingElse = (SymTabAST)slist.getNextSibling();
645
646 symbolTable.pushScope( block );
647 walkTree( slist, false );
648 symbolTable.popScope();
649
650 walkSiblings( everythingElse, false );
651 }
652
653 /**
654 * process the given SymTabAST as a finally block
655 *
656 * @param tree the SymTabAST to process
657 * @return <code>void</code>
658 * @see #makeBlock(SymTabAST)
659 * @see #walkTree(SymTabAST, boolean)
660 */
661 public void processFinally(SymTabAST tree){
662 BlockDef block = makeBlock(tree);
663
664 SymTabAST slist = tree.findFirstToken(TokenTypes.SLIST);
665 SymTabAST tryBlock = tree.findFirstToken(TokenTypes.LITERAL_TRY);
666
667 symbolTable.pushScope( block );
668 walkTree( slist, false );
669 symbolTable.popScope();
670
671 walkTree( tryBlock, false );
672 }
673
674
675 /**
676 * process the given SymTabAST as a method definition
677 *
678 * @param tree the SymTabAST to process
679 * @return <code>void</code>
680 * @see net.sourceforge.transmogrify.symtab.MethodDef
681 * @see net.sourceforge.transmogrify.symtab.SymbolTable
682 * @see #walkTree(SymTabAST, boolean)
683 */
684 public void processMethodDef(SymTabAST tree) {
685 String name = tree.findFirstToken(TokenTypes.IDENT).getText();
686 MethodDef method = new MethodDef(name, symbolTable.getCurrentScope(),
687 tree);
688 symbolTable.defineMethod( method );
689
690 symbolTable.pushScope( method );
691 walkTree(tree.findFirstToken(TokenTypes.SLIST), false);
692 symbolTable.popScope();
693 }
694
695 /**
696 * process the given SymTabAST as a constructor definition
697 *
698 * @param tree the SymTabAST to process
699 * @return <code>void</code>
700 * @see net.sourceforge.transmogrify.symtab.MethodDef
701 * @see net.sourceforge.transmogrify.symtab.SymbolTable
702 * @see #walkTree(SymTabAST, boolean)
703 */
704 public void processConstructorDef(SymTabAST tree) {
705 String name = tree.findFirstToken(TokenTypes.IDENT).getText();
706 MethodDef constructor = new MethodDef(name, symbolTable.getCurrentScope(),
707 tree);
708 symbolTable.defineMethod( constructor );
709
710 symbolTable.pushScope( constructor );
711 walkTree(tree.findFirstToken(TokenTypes.SLIST), false);
712 symbolTable.popScope();
713 }
714
715 /**
716 * process the given SymTabAST as a for block
717 *
718 * @param tree the SymTabAST to process
719 * @return <code>void</code>
720 * @see #makeBlock(SymTabAST)
721 * @see #walkTree(SymTabAST, boolean)
722 */
723 public void processFor(SymTabAST tree) {
724 BlockDef block = makeBlock((SymTabAST)tree);
725
726 symbolTable.pushScope( block );
727 walkTree(tree.findFirstToken(TokenTypes.FOR_INIT), false);
728 walkTree(tree.findFirstToken(TokenTypes.FOR_CONDITION), false);
729
730 SymTabAST forIter = tree.findFirstToken(TokenTypes.FOR_ITERATOR);
731 walkTree(forIter, false);
732
733 SymTabAST body = (SymTabAST)forIter.getNextSibling();
734 //handle Checkstyle grammar
735 if (body.getType() == TokenTypes.RPAREN) {
736 body = (SymTabAST) body.getNextSibling();
737 }
738 walkTree(body, false);
739 symbolTable.popScope();
740 }
741
742 /**
743 * process the given SymTabAST as an if block
744 *
745 * @param tree the SymTabAST to process
746 * @return <code>void</code>
747 * @see #makeBlock(SymTabAST)
748 * @see #walkTree(SymTabAST, boolean)
749 * @see #processElse(SymTabAST)
750 */
751 public void processIf(SymTabAST tree) {
752 BlockDef block = makeBlock((SymTabAST)tree);
753
754 SymTabAST expr = tree.findFirstToken(TokenTypes.EXPR);
755 SymTabAST ifBranch = (SymTabAST)expr.getNextSibling();
756 // handle Checkstyle grammar
757 if (ifBranch.getType() == TokenTypes.RPAREN) {
758 ifBranch = (SymTabAST) ifBranch.getNextSibling();
759 }
760 SymTabAST elseBranch = (SymTabAST)ifBranch.getNextSibling();
761 // handle Checkstyle grammar
762 if ((elseBranch != null) && (elseBranch.getType() == TokenTypes.SEMI)) {
763 elseBranch = (SymTabAST) elseBranch.getNextSibling();
764 }
765 if ((elseBranch != null) && (elseBranch.getType() == TokenTypes.LITERAL_ELSE)) {
766 elseBranch = (SymTabAST) elseBranch.getFirstChild();
767 }
768
769 symbolTable.pushScope( block );
770 walkTree(expr, false);
771 walkTree(ifBranch, false);
772 symbolTable.popScope();
773
774 processElse(elseBranch);
775 }
776
777 /**
778 * process the given SymTabAST as an else block
779 *
780 * @param tree the SymTabAST to process
781 * @return <code>void</code>
782 * @see #processIf(SymTabAST)
783 * @see #makeElseBlock(SymTabAST)
784 */
785 public void processElse(SymTabAST tree) {
786 if (tree != null) {
787 if (tree.getType() == TokenTypes.LITERAL_IF) {
788 processIf(tree);
789 }
790 else {
791 makeElseBlock(tree);
792 }
793 }
794 }
795
796 /**
797 * defines an anonymous block to enclose the scope of an else block
798 *
799 * @param tree the SymTabAST to process
800 * @return <code>void</code>
801 * @see #makeBlock(SymTabAST)
802 * @see #walkTree(SymTabAST, boolean)
803 */
804 public void makeElseBlock(SymTabAST tree) {
805 if (tree.getType() == TokenTypes.SLIST) {
806 BlockDef block = makeBlock((SymTabAST)tree);
807 symbolTable.pushScope( block );
808 walkTree(tree, false);
809 symbolTable.popScope();
810 }
811 else {
812 walkTree(tree, false);
813 }
814 }
815
816 /**
817 * processes the current tree node as BlockDef
818 * @param tree current tree node
819 * @param makeAnonymousScopes
820 * @return <code>void</code>
821 */
822 public void processBlock(SymTabAST tree, boolean makeAnonymousScopes) {
823 BlockDef block = makeBlock((SymTabAST)tree);
824 symbolTable.pushScope(block);
825 //handle Checkstyle grammar
826 SymTabAST child = (SymTabAST)tree.getFirstChild();
827 if ((child != null) && (child.getType() == TokenTypes.LPAREN)) {
828 child = (SymTabAST) child.getNextSibling();
829 }
830 walkSiblings((SymTabAST)child, makeAnonymousScopes);
831 symbolTable.popScope();
832 }
833
834 /**
835 * set the current file to the named file
836 *
837 * @param fileName the name of the file
838 * @return <code>void</code>
839 */
840 public void setCurrentFile(String fileName) {
841 currentFile = new File(fileName);
842 symbolTable.setCurrentFile(currentFile);
843 }
844
845
846 /**
847 * creates a new <code> BlockDef </code> in the SymbolTable
848 *
849 * @param tree is a <code> SymTabAST </code> whose root begins the new block
850 * @return <code> BlockDef </code>
851 * @see net.sourceforge.transmogrify.symtab.BlockDef
852 * @see net.sourceforge.transmogrify.symtab.SymbolTable
853 */
854 private BlockDef makeBlock( SymTabAST tree ) {
855 BlockDef block = new BlockDef( symbolTable.getCurrentScope(), tree );
856 symbolTable.defineBlock( block );
857 return block;
858 }
859
860 /**
861 * returns the <code>SymTabAST</code> that contains the parameter classDef's
862 * extends nodes
863 *
864 * @param classDef is a <code> ClassDef </code> whose extends nodes we want
865 * @return <code> SymTabAST </code>
866 */
867 public static SymTabAST getExtendsNode(ClassDef classDef) {
868 SymTabAST result = null;
869 SymTabAST extendsClause = null;
870
871 SymTabAST classDefTreeNode = classDef.getTreeNode();
872 extendsClause =
873 classDefTreeNode.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
874
875 if (extendsClause != null) {
876 result = (SymTabAST)(extendsClause.getFirstChild());
877 }
878
879 return result;
880 }
881
882 /**
883 * Superclass for different kind of XXXFinisher subclass
884 * @see CatchFinisher
885 * @see ClassFinisher
886 * @see DefinitionFinisher
887 * @see MethodFinisher
888 * @see VariableFinisher
889 */
890 class DefinitionFinisher {
891
892 protected SymTabAST _node = null;
893
894 /**
895 * Constructor. It finishes the definition passed to it
896 *
897 */
898 public DefinitionFinisher( Definition def ) {
899 _node = def.getTreeNode();
900 }
901
902 public void finish() throws SymbolTableException {}
903
904 /**
905 * gets the classDef that represents the type of the given definition
906 *
907 *
908 * @param def the definition whose type to find
909 * @param typeNode the TokenTypes.TYPE node associated with the def
910 *
911 * @return the resulting class definition
912 */
913 protected IClass getType( Definition def, SymTabAST typeNode ) {
914 IClass result = null;
915
916 SymTabAST typeClassNode = null;
917 boolean isArray = false;
918
919 if ( typeNode.getFirstChild().getType()
920 == TokenTypes.ARRAY_DECLARATOR ) {
921 isArray = true;
922 typeClassNode = (SymTabAST)typeNode.getFirstChild().getFirstChild();
923 }
924 else {
925 typeClassNode = (SymTabAST)typeNode.getFirstChild();
926 }
927
928 Scope lookupScope = null;
929
930 if (def instanceof Scope) {
931 lookupScope = (Scope)def;
932 }
933 else {
934 lookupScope = def.getParentScope();
935 }
936
937 Resolver resolver = new Resolver(symbolTable);
938 IClass typeClass = resolver.resolveClass(typeClassNode, lookupScope, null, false);
939
940 if ( isArray ) {
941 result = new ArrayDef( typeClass );
942 }
943 else {
944 result = typeClass;
945 }
946
947 return result;
948 }
949
950 }
951
952 class ClassFinisher extends DefinitionFinisher {
953
954 private ClassDef _def = null;
955
956 /**
957 * Constructor. Creates a ClassFinisher from a <code> Definition </code>
958 *
959 * @param def is a <code> Definition </code>
960 */
961 public ClassFinisher( Definition def ) {
962 super( def );
963 _def = (ClassDef)def;
964 }
965
966 /**
967 * Completes all tasks required for finishing a ClassDef
968 * Including adding imports, setting super classes and adding
969 * interfaces.
970 * @return <code>void</code>
971 * @throws <code>SymbolTableException</code>
972 * @see #addImports()
973 * @see #setSuperclass()
974 * @see #addInterfaces()
975 */
976 public void finish() throws SymbolTableException {
977 if ( _node != null ) {
978 addImports();
979 setSuperclass();
980 addInterfaces();
981 }
982 }
983
984 /**
985 * Adds all packages and classes that are imported by this class
986 * to the class for later reference
987 */
988 private void addImports() throws ClassImportException {
989 IPackage java = new ExternalPackage("java", null);
990 IPackage lang = new ExternalPackage("lang", java);
991 java.addDefinition(lang);
992 _def.importPackage(lang);
993
994 Vector unprocessedImports = _def.getUnprocessedImports();
995 for ( int i = 0; i < unprocessedImports.size(); i++ ) {
996 SymTabAST importNode = (SymTabAST)unprocessedImports.get(i);
997 SymTabAST imported = (SymTabAST)importNode.getFirstChild();
998 SymTabAST lastPart = (SymTabAST)imported.getFirstChild().getNextSibling();
999
1000 DotIterator it = new DotIterator(imported);
1001 SymTabAST current = null;
1002 String className = null;
1003 IClass importedClass = null;
1004
1005 // attempt at each token to find the class
1006 // first in source
1007 // then on classpath
1008 //
1009 // if there are more tokens left
1010 // continue until you hit the last token
1011 // if it's a star
1012 // import all inner classes
1013 // else
1014 // import the explicitly named inner class
1015 // else import the class
1016 //
1017 // if no classes were found, import the package
1018
1019 while(it.hasNext()) {
1020 current = it.nextNode();
1021 if (className == null) {
1022 className = current.getText();
1023 }
1024 else {
1025 if (!current.getText().equals("*")) {
1026 className += "." + current.getText();
1027 }
1028 else {
1029 break;
1030 }
1031 }
1032 importedClass = findOrLoadClass(className, importedClass);
1033
1034 if (importedClass != null) {
1035 break;
1036 }
1037 }
1038
1039 if (it.hasNext()) {
1040 boolean isImported = false;
1041 while(it.hasNext()) {
1042 current = it.nextNode();
1043 if (current.getText().equals("*")) {
1044 importInnerClasses(importedClass);
1045 isImported = true;
1046 }
1047 else {
1048 className += "$" + current.getText();
1049 importedClass = findOrLoadClass(className, importedClass);
1050 }
1051 }
1052 if (!isImported) {
1053 _def.importClass(importedClass);
1054 }
1055 }
1056 else {
1057 if (importedClass != null) {
1058 _def.importClass(importedClass);
1059 }
1060 else {
1061 if (current != null && current.getText().equals("*")) {
1062 IPackage pkg = symbolTable.getPackage(className);
1063 if (pkg == null) {
1064 pkg = getPackage(className);
1065 }
1066 _def.importPackage(pkg);
1067 }
1068 else {
1069 //TODO: can we safely ignore this?
1070 //throw new ClassImportException(className);
1071 ;
1072 }
1073 }
1074 }
1075
1076 // now set definitions where appropriate
1077 imported.ignoreChildren();
1078 if ((lastPart.getType() == TokenTypes.IDENT)
1079 //TODO: guard added for class not loaded
1080 //This is OK for single file processing, but not
1081 //multiple files.
1082 && (importedClass != null)
1083 )
1084 {
1085 lastPart.setDefinition(importedClass, null, true);
1086 lastPart.setMeaningfulness(true);
1087 }
1088 }
1089 }
1090
1091 /**
1092 * creates <code>ExternalPackage</code>
1093 * @param packageName name of the package
1094 * @return <code>ExternalPackage</code>
1095 * @see net.sourceforge.transmogrify.symtab.ExternalPackage
1096 */
1097 private ExternalPackage getPackage(String packageName) {
1098 return new ExternalPackage(packageName, null);
1099 }
1100
1101 /**
1102 * stores the inner classes in the approriate ClassDef
1103 * @param outerClass
1104 * @return <code>void</code>
1105 */
1106 private void importInnerClasses(IClass outerClass) {
1107 IClass[] innerClasses = outerClass.getInnerClasses();
1108
1109 for (int i = 0; i < innerClasses.length; i++) {
1110 _def.importClass(innerClasses[i]);
1111 }
1112 }
1113
1114 /**
1115 * creates <code>ExternalClass</code> based on a java class
1116 * @param className class to be loaded
1117 * @return <code>IClass</code>
1118 * @see net.sourceforge.transmogrify.symtab.ExternalClass
1119 */
1120 private IClass loadClass(String className) {
1121 IClass result = null;
1122
1123 try {
1124 Class javaClass
1125 = ClassManager.getClassLoader().loadClass(className);
1126
1127 result = new ExternalClass(javaClass);
1128 }
1129 catch (ClassNotFoundException ignoreMe) {}
1130
1131 return result;
1132 }
1133
1134 /**
1135 * find a class from <code>BaseCode</code> or its parent
1136 * @param className name of the class to be load or found
1137 * @param parentClass its parent class
1138 * @return <code>IClass</code>
1139 * @see net.sourceforge.transmogrify.symtab.SymbolTable
1140 * @see net.sourceforge.transmogrify.symtab.IClass
1141 * @see #loadClass(String)
1142 */
1143 private IClass findOrLoadClass(String className, IClass parentClass) {
1144 IClass result = null;
1145
1146 if (parentClass == null) {
1147 result = symbolTable.getBaseScope().getClassDefinition(className);
1148 }
1149 else {
1150 int index = className.lastIndexOf("$");
1151 if (index < 0) {
1152 index = className.lastIndexOf(".");
1153 }
1154
1155 result = parentClass.getClassDefinition(className.substring(index + 1));
1156 }
1157
1158 if (result == null) {
1159 result = loadClass(className);
1160 }
1161
1162 return result;
1163 }
1164
1165 /**
1166 *
1167 * If the class has a super class a reference to this super class
1168 * is added to this class.
1169 * @return <code>void</code>
1170 * @see net.sourceforge.transmogrify.symtab.ClassDef
1171 */
1172 private void setSuperclass() {
1173 if (_def.getTreeNode().getType() == TokenTypes.CLASS_DEF) {
1174 SymTabAST extendsNode = getExtendsNode(_def);
1175 if ( extendsNode != null ) {
1176 String superclassName = ASTUtil.constructDottedName(extendsNode);
1177 IClass superclass = _def.getClassDefinition(superclassName);
1178 if ( superclass != null ) {
1179 _def.setSuperclass( superclass );
1180 superclass.addSubclass( _def );
1181 }
1182 }
1183 else {
1184 _def.setSuperclass(new ExternalClass(Object.class));
1185 }
1186 }
1187 else {
1188 _def.setSuperclass(new ExternalClass(Object.class));
1189 }
1190 }
1191
1192 /**
1193 *
1194 * If the class implements an interface a reference to this interface
1195 * is added to this class.
1196 * @return <code>void</code>
1197 * @see net.sourceforge.transmogrify.symtab.ClassDef
1198 */
1199 private void addInterfaces() {
1200 SymTabAST implementsClause = null;
1201
1202 if (_def.getTreeNode().getType() == TokenTypes.CLASS_DEF) {
1203 implementsClause
1204 = _node.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);
1205 }
1206 else {
1207 implementsClause
1208 = _node.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
1209 }
1210
1211 if ( implementsClause != null ) {
1212 SymTabAST interfaceNode = (SymTabAST)implementsClause.getFirstChild();
1213 while ( interfaceNode != null ) {
1214 IClass implemented =
1215 _def.getClassDefinition(interfaceNode.getText());
1216 if ( implemented != null ) {
1217 _def.addInterface( implemented );
1218 implemented.addImplementor( _def );
1219 }
1220 interfaceNode = (SymTabAST)(interfaceNode.getNextSibling());
1221 }
1222 }
1223 }
1224 }
1225
1226 /**
1227 *
1228 * Completes a Variable by setting all required references
1229 *
1230 */
1231 class VariableFinisher extends DefinitionFinisher {
1232 VariableDef _def = null;
1233
1234
1235 /**
1236 *
1237 * Constructor. Creates a VariableFinishes from the <code>Definition</code>
1238 * @param def VariableDef to be finished
1239 * @see net.sourceforge.transmogrify.symtab.VariableDef
1240 */
1241 public VariableFinisher( Definition def ) {
1242 super( def );
1243 _def = (VariableDef)def;
1244 }
1245
1246
1247 /**
1248 *
1249 * Finishes the variable by setting the Type
1250 * @return <code>void</code>
1251 * @see #getType(Definition, SymTabAST)
1252 * @see net.sourceforge.transmogrify.symtab.VariableDef
1253 */
1254 public void finish() {
1255
1256 SymTabAST typeNode = _node.findFirstToken(TokenTypes.TYPE);
1257
1258 SymTabAST typeTextNode = (SymTabAST)(typeNode.getFirstChild());
1259 if ( typeTextNode.getType() == TokenTypes.ARRAY_DECLARATOR ) {
1260 typeTextNode = (SymTabAST)(typeTextNode.getFirstChild());
1261 }
1262 typeTextNode.setLine(ASTUtil.getLine( _def.getTreeNode() ));
1263
1264 IClass varType = getType(_def, typeNode);
1265 _def.setType( varType );
1266
1267 }
1268 }
1269
1270 /**
1271 *
1272 * Completes a Variable by setting all required references
1273 *
1274 */
1275 class MethodFinisher extends DefinitionFinisher {
1276 MethodDef _def = null;
1277
1278
1279 /**
1280 *
1281 * Creates a MethodFinisher from a <code> Definition <code>
1282 * @param def MethodDef to be finished
1283 * @see net.sourceforge.transmogrify.symtab.MethodDef
1284 */
1285 public MethodFinisher( Definition def ) {
1286 super( def );
1287 _def = (MethodDef)def;
1288 }
1289
1290 /**
1291 *
1292 * Completes a method by setting all references to return types,
1293 * signatures and exceptions.
1294 * @return <code>void</code>
1295 * @see #setReturnType()
1296 * @see #setSignature()
1297 * @see #setExceptionsThrown()
1298 */
1299 public void finish() {
1300 setReturnType();
1301 setSignature();
1302 setExceptionsThrown();
1303 }
1304
1305 /**
1306 *
1307 * setReturnType adds a reference to the methods return type
1308 * to the method definition
1309 * @return <code>void</code>
1310 * @see net.sourceforge.transmogrify.symtab.MethodDef
1311 * @see #getType(Definition, SymTabAST)
1312 * @see #getTypeNode()
1313 */
1314 private void setReturnType() {
1315 IClass type = null;
1316
1317 if ( isConstructor() ) {
1318 type = _def.getEnclosingClass();
1319 }
1320 else {
1321 type = getType(_def, getTypeNode());
1322 }
1323
1324 _def.setType(type);
1325 }
1326
1327 /**
1328 *
1329 * setSignature adds a reference to the methods paramaters
1330 * to the method definition
1331 * @return <code>void</code>
1332 * @see #makeVariableDef(SymTabAST, Definition)
1333 * @see VariableFinisher
1334 * @see net.sourceforge.transmogrify.symtab.MethodDef
1335 */
1336 private void setSignature() {
1337 SymTabAST parametersNode
1338 = _node.findFirstToken(TokenTypes.PARAMETERS);
1339
1340 SymTabAST parameterNode = (SymTabAST)(parametersNode.getFirstChild());
1341 while ( parameterNode != null ) {
1342 if (parameterNode.getType() == TokenTypes.PARAMETER_DEF) {
1343 VariableDef parameter = makeVariableDef( parameterNode, _def );
1344 new VariableFinisher( parameter ).finish();
1345