View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.plsql.symboltable;
5   
6   import java.util.Stack;
7   import java.util.logging.Logger;
8   
9   import net.sourceforge.pmd.lang.plsql.ast.ASTBlock;
10  import net.sourceforge.pmd.lang.plsql.ast.ASTDeclarativeUnit;
11  import net.sourceforge.pmd.lang.plsql.ast.ASTForAllStatement;
12  import net.sourceforge.pmd.lang.plsql.ast.ASTForStatement;
13  import net.sourceforge.pmd.lang.plsql.ast.ASTID;
14  import net.sourceforge.pmd.lang.plsql.ast.ASTInput;
15  import net.sourceforge.pmd.lang.plsql.ast.ASTMethodDeclarator;
16  import net.sourceforge.pmd.lang.plsql.ast.ASTObjectDeclaration;
17  import net.sourceforge.pmd.lang.plsql.ast.ASTObjectNameDeclaration;
18  import net.sourceforge.pmd.lang.plsql.ast.ASTPackageBody;
19  import net.sourceforge.pmd.lang.plsql.ast.ASTPackageSpecification;
20  import net.sourceforge.pmd.lang.plsql.ast.ASTProgramUnit;
21  import net.sourceforge.pmd.lang.plsql.ast.ASTTriggerTimingPointSection;
22  import net.sourceforge.pmd.lang.plsql.ast.ASTTriggerUnit;
23  import net.sourceforge.pmd.lang.plsql.ast.ASTTypeMethod;
24  import net.sourceforge.pmd.lang.plsql.ast.ASTTypeSpecification;
25  import net.sourceforge.pmd.lang.plsql.ast.ASTVariableOrConstantDeclaratorId;
26  import net.sourceforge.pmd.lang.plsql.ast.PLSQLNode;
27  import net.sourceforge.pmd.lang.plsql.ast.PLSQLParserVisitorAdapter;
28  import net.sourceforge.pmd.lang.symboltable.Scope;
29  
30  /**
31   * Visitor for scope creation.
32   * Visits all nodes of an AST and creates scope objects for nodes representing
33   * syntactic entities which may contain declarations. For example, a block
34   * may contain variable definitions (which are declarations) and
35   * therefore needs a scope object where these declarations can be associated,
36   * whereas an expression can't contain declarations and therefore doesn't need
37   * a scope object.
38   * With the exception of global scopes, each scope object is linked to its
39   * parent scope, which is the scope object of the next embedding syntactic
40   * entity that has a scope.
41   */
42  public class ScopeAndDeclarationFinder extends PLSQLParserVisitorAdapter {
43      private final static Logger LOGGER = Logger.getLogger(ScopeAndDeclarationFinder.class.getName()); 
44  
45      /**
46       * A stack of scopes reflecting the scope hierarchy when a node is visited.
47       * This is used to set the parents of the created scopes correctly.
48       */
49      private Stack<Scope> scopes = new Stack<Scope>();
50  
51      /**
52       * Sets the scope of a node and adjusts the scope stack accordingly.
53       * The scope on top of the stack is set as the parent of the given scope,
54       * which is then also stored on the scope stack.
55       *
56       * @param newScope the scope for the node.
57       * @param node     the AST node for which the scope is to be set.
58       * @throws java.util.EmptyStackException if the scope stack is empty.
59       */
60      private void addScope(Scope newScope, PLSQLNode node) {
61  	newScope.setParent(scopes.peek());
62  	scopes.push(newScope);
63  	node.setScope(newScope);
64      }
65  
66      /**
67       * Creates a new local scope for an AST node.
68       * The scope on top of the stack is set as the parent of the new scope,
69       * which is then also stored on the scope stack.
70       *
71       * @param node the AST node for which the scope has to be created.
72       * @throws java.util.EmptyStackException if the scope stack is empty.
73       */
74      private void createLocalScope(PLSQLNode node) {
75  	addScope(new LocalScope(), node);
76      }
77  
78      /**
79       * Creates a new method scope for an AST node.
80       * The scope on top of the stack is set as the parent of the new scope,
81       * which is then also stored on the scope stack.
82       *
83       * @param node the AST node for which the scope has to be created.
84       * @throws java.util.EmptyStackException if the scope stack is empty.
85       */
86      private void createMethodScope(PLSQLNode node) {
87  	addScope(new MethodScope(node), node);
88      }
89  
90      /**
91       * Creates a new class scope for an AST node.
92       * The scope on top of the stack is set as the parent of the new scope,
93       * which is then also stored on the scope stack.
94       *
95       * @param node the AST node for which the scope has to be created.
96       * @throws java.util.EmptyStackException if the scope stack is empty.
97       */
98      private void createClassScope(PLSQLNode node) {
99  	if (node instanceof ASTDeclarativeUnit) {
100 	    addScope(new ClassScope(), node);
101 	} else {
102 	    addScope(new ClassScope(node.getImage()), node);
103 	}
104     }
105 
106     /**
107      * Creates a new global scope for an AST node.
108      * The new scope is stored on the scope stack.
109      *
110      * @param node the AST node for which the scope has to be created.
111      */
112     private void createSourceFileScope(ASTInput node) {
113 	// When we do full symbol resolution, we'll need to add a truly top-level GlobalScope.
114 	Scope scope;
115 	//%TODO generate a SchemaScope, based on inferred or explcitly specified SchemaName 
116 	ASTObjectDeclaration n = null; // node.getPackageDeclaration();
117 	if (n != null) {
118 	    scope = new SourceFileScope(n.jjtGetChild(0).getImage());
119 	} else {
120 	    scope = new SourceFileScope();
121 	}
122 	scopes.push(scope);
123 	node.setScope(scope);
124     }
125 
126     @Override
127     public Object visit(ASTInput node, Object data) {
128 	createSourceFileScope(node);
129 	cont(node);
130 	return data;
131     }
132 
133     @Override
134     public Object visit(ASTPackageSpecification node, Object data) {
135 	createClassScope(node);
136 	Scope s = ((PLSQLNode)node.jjtGetParent()).getScope();
137 	s.addDeclaration(new ClassNameDeclaration(node));
138 	cont(node);
139 	return data;
140     }
141 
142     @Override
143     public Object visit(ASTPackageBody node, Object data) {
144 	createClassScope(node);
145 	Scope s = ((PLSQLNode)node.jjtGetParent()).getScope();
146 	s.addDeclaration(new ClassNameDeclaration(node));
147 	cont(node);
148 	return data;
149     }
150 
151 
152     @Override
153     public Object visit(ASTTypeSpecification node, Object data) {
154 	createClassScope(node);
155 	Scope s = ((PLSQLNode)node.jjtGetParent()).getScope();
156 	s.addDeclaration(new ClassNameDeclaration(node));
157 	cont(node);
158 	return data;
159     }
160 
161     @Override
162     public Object visit(ASTTriggerUnit node, Object data) {
163 	createClassScope(node);
164 	Scope s = ((PLSQLNode)node.jjtGetParent()).getScope();
165 	s.addDeclaration(new ClassNameDeclaration(node));
166 	cont(node);
167 	return data;
168     }
169 
170     /*
171     @Override
172     public Object visit(ASTCompoundTriggerBlock node, Object data) {
173 	createMethodScope(node);
174 	ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class);
175 	node.getScope().getEnclosingClassScope().addDeclaration(new MethodNameDeclaration(md));
176 	cont(node);
177 	return data;
178     }
179     */
180 
181     @Override
182     public Object visit(ASTTriggerTimingPointSection node, Object data) {
183 	createMethodScope(node);
184 	//Treat a Timing Point Section like a packaged FUNCTION or PROCEDURE
185 	node.getScope().getEnclosingScope(ClassScope.class).addDeclaration(new MethodNameDeclaration(node));
186 	cont(node);
187 	return data;
188     }
189 
190     //@Override
191     //public Object visit(ASTEnumDeclaration node, Object data) {
192 	//createClassScope(node);
193 	//cont(node);
194 	//return data;
195     //}
196 
197     //@Override
198     //public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
199 	//createClassScope(node);
200 	//cont(node);
201 	//return data;
202     //}
203 
204     @Override
205     public Object visit(ASTObjectDeclaration node, Object data) {
206         super.visit(node, data);
207 	return data;
208     }
209 
210     @Override
211     public Object visit(ASTBlock node, Object data) {
212 	createLocalScope(node);
213 	cont(node);
214 	return data;
215     }
216 
217 
218 
219     /*
220     @Override
221     public Object visit(ASTMethodDeclaration node, Object data) {
222 	createMethodScope(node);
223 	//
224 	 // A method declaration my be-
225 	 //   ASTProgramUnit - a standalone or packaged FUNCTION or PROCEDURE 
226 	 //   ASTTypeMethod - an OBJECT TYPE method  
227 	 //  
228 	 //  The Method declarator is below the  ASTProgramUnit / ASTTypeMethod 
229 	 ///
230 	List<ASTMethodDeclarator> methodDeclarators = node.findDescendantsOfType(ASTMethodDeclarator.class);
231 	if (!methodDeclarators.isEmpty() )
232 	{
233 	  //Use first Declarator in the list 
234 	  ASTMethodDeclarator md = methodDeclarators.get(0);
235             LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" 
236 		               + node.getMethodName()
237 		               + "; Image=" + node.getImage()
238 		              );
239 	   
240 	}
241 	//ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class);
242 	// A PLSQL Method (FUNCTION|PROCEDURE) may be schema-level 
243 	try
244 	{
245 	  node.getScope().getEnclosingClassScope().addDeclaration(new MethodNameDeclaration(md));
246 	}
247 	catch (Exception e)
248 	{
249 	  //@TODO possibly add to a pseudo-ClassScope equivalent to the Schema name 
250 	  LOGGER.finest("ProgramUnit getEnclosingClassScope Exception string=\""+e.getMessage()+"\"");
251 	  if("getEnclosingClassScope() called on SourceFileScope".equals(e.getMessage()))
252 	  {
253             LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" 
254 		               + node.getMethodName()
255 		               + "; Image=" + node.getImage()
256 		              );
257 	   
258 	    //A File-level/Schema-level object may have a Schema-name explicitly specified in the declaration 
259 	    ASTObjectNameDeclaration on = md.getFirstChildOfType(ASTObjectNameDeclaration.class);
260 	    if( 1 < on.jjtGetNumChildren())
261 	    {
262               ASTID schemaName = on.getFirstChildOfType(ASTID.class);
263 	      LOGGER.finest("SchemaName for Schema-level method: methodName=" 
264 				 + node.getMethodName()
265 				 + "; Image=" + node.getImage()
266 				 + "is " + schemaName.getImage()
267 				);
268 	     
269 	    }
270 	  }
271 	}
272 	cont(node);
273 	return data;
274     }
275     */
276 
277 @Override
278 public Object visit(ASTTypeMethod node, Object data) {
279 	createMethodScope(node);
280 	ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class);
281 	// A PLSQL Method (FUNCTION|PROCEDURE) may be schema-level 
282 	try
283 	{
284 	  node.getScope().getEnclosingScope(ClassScope.class).addDeclaration(new MethodNameDeclaration(md));
285 	}
286 	catch (Exception e)
287 	{
288 	  //@TODO possibly add to a pseudo-ClassScope equivalent to the Schema name 
289 	  LOGGER.finest("ProgramUnit getEnclosingClassScope Exception string=\""+e.getMessage()+"\"");
290 	  if("getEnclosingClassScope() called on SourceFileScope".equals(e.getMessage()))
291 	  {
292             LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" 
293 		               + node.getMethodName()
294 		               + "; Image=" + node.getImage()
295 		              );
296 	   
297 	    //A File-level/Schema-level object may have a Schema-name explicitly specified in the declaration 
298 	    ASTObjectNameDeclaration on = md.getFirstChildOfType(ASTObjectNameDeclaration.class);
299 	    if( 1 < on.jjtGetNumChildren())
300 	    {
301               ASTID schemaName = on.getFirstChildOfType(ASTID.class);
302 	      LOGGER.finest("SchemaName for Schema-level method: methodName=" 
303 				 + node.getMethodName()
304 				 + "; Image=" + node.getImage()
305 				 + "is " + schemaName.getImage()
306 				);
307 	     
308 	    }
309 	  }
310 	}
311 	cont(node);
312 	return data;
313   }
314 
315     @Override
316     public Object visit(ASTProgramUnit node, Object data) {
317 	createMethodScope(node);
318 	ASTMethodDeclarator md = node.getFirstChildOfType(ASTMethodDeclarator.class);
319 	// A PLSQL Method (FUNCTION|PROCEDURE) may be schema-level 
320 	try
321 	{
322 	  node.getScope().getEnclosingScope(ClassScope.class).addDeclaration(new MethodNameDeclaration(md));
323 	}
324 	catch (Exception e)
325 	{
326 	  //@TODO possibly add to a pseudo-ClassScope equivalent to the Schema name 
327 	  LOGGER.finest("ProgramUnit getEnclosingClassScope Exception string=\""+e.getMessage()+"\"");
328 	  if("getEnclosingClassScope() called on SourceFileScope".equals(e.getMessage()))
329 	  {
330             LOGGER.finest("ClassScope skipped for Schema-level method: methodName=" 
331 		               + node.getMethodName()
332 		               + "; Image=" + node.getImage()
333 		              );
334 	   
335 	    //A File-level/Schema-level object may have a Schema-name explicitly specified in the declaration 
336 	    ASTObjectNameDeclaration on = md.getFirstChildOfType(ASTObjectNameDeclaration.class);
337 	    if( 1 < on.jjtGetNumChildren())
338 	    {
339               ASTID schemaName = on.getFirstChildOfType(ASTID.class);
340 	      LOGGER.finest("SchemaName for Schema-level method: methodName=" 
341 				 + node.getMethodName()
342 				 + "; Image=" + node.getImage()
343 				 + "is " + schemaName.getImage()
344 				);
345 	     
346 	    }
347 	  }
348 	}
349 	cont(node);
350 	return data;
351     }
352 
353     // TODO - what about while loops and do loops?
354     @Override
355     public Object visit(ASTForStatement node, Object data) {
356 	createLocalScope(node);
357 	cont(node);
358 	return data;
359     }
360 
361     @Override
362     public Object visit(ASTForAllStatement node, Object data) {
363 	createLocalScope(node);
364 	cont(node);
365 	return data;
366     }
367 
368     @Override
369     public Object visit(ASTVariableOrConstantDeclaratorId node, Object data) {
370 	VariableNameDeclaration decl = new VariableNameDeclaration(node);
371 	node.getScope().addDeclaration(decl);
372 	node.setNameDeclaration(decl);
373 	return super.visit(node, data);
374     }
375 
376     //@Override
377     //public Object visit(ASTSwitchStatement node, Object data) {
378 	//createLocalScope(node);
379 	//cont(node);
380 	//return data;
381     //}
382 
383     private void cont(PLSQLNode node) {
384 	super.visit(node, null);
385 	scopes.pop();
386     }
387 }