KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > repl > JavaDebugInterpreter


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.repl;
35
36 import koala.dynamicjava.interpreter.error.*;
37 import koala.dynamicjava.interpreter.*;
38 import koala.dynamicjava.interpreter.context.*;
39 import koala.dynamicjava.tree.*;
40
41 import java.util.List JavaDoc;
42 import java.util.LinkedList JavaDoc;
43 import java.util.Iterator JavaDoc;
44 import java.util.StringTokenizer JavaDoc;
45 import java.lang.reflect.*;
46
47 import edu.rice.cs.drjava.model.repl.newjvm.ClassPathManager;
48 import edu.rice.cs.util.UnexpectedException;
49
50 /** This class is an extension to DynamicJavaAdapter that allows us to process expressions involving the "this"
51  * keyword correctly in the current debug interpreter context. This allows users to debug outer classes and their
52  * fields using the usual Java syntax of outerclass.this. This is done by holding on to the class name of "this"
53  * and by translating references to outer instance classes to field accesses in the form "this.this$N.this$N-1...".
54  *
55  * This class is loaded in the Interpreter JVM, not the Main JVM.
56  * (Do not use DrJava's config framework here.)
57  *
58  * @version $Id: JavaDebugInterpreter.java 4011 2006-09-22 15:31:42Z rcartwright $
59  */

60 public class JavaDebugInterpreter extends DynamicJavaAdapter {
61
62   /** This interpreter's name. */
63   protected final String JavaDoc _name;
64
65   /** The class name of the "this" object for the currently suspended thread. */
66   protected String JavaDoc _thisClassName;
67
68   /** The name of the package containing _this, if any. */
69   protected String JavaDoc _thisPackageName;
70
71   /** Extends IdentityVisitor to convert all instances of ThisExpressions in the tree to either QualifiedName or an
72    * ObjectFieldAccess
73    */

74   protected IdentityVisitor _translationVisitor;
75
76   /** Creates a new debug interpreter.
77    * @param name the name of the interpreter
78    * @param className the class name of the current context of "this"
79    */

80   public JavaDebugInterpreter(String JavaDoc name, String JavaDoc className) {
81     super(new ClassPathManager());
82     _name = name;
83     setClassName(className);
84     _translationVisitor = makeTranslationVisitor();
85   }
86
87   /** Processes the tree before evaluating it. The translation visitor visits each node in the tree for the given
88    * statement or expression and converts the necessary nodes.
89    * @param node Tree to process
90    */

91   public Node processTree(Node node) { return node.acceptVisitor(_translationVisitor); }
92
93   public GlobalContext makeGlobalContext(TreeInterpreter i) {
94     return new GlobalContext(i) {
95       public boolean exists(String JavaDoc name) {
96         return (super.exists(name)) ||
97           (_getObjectFieldAccessForField(name, this) != null) ||
98           (_getStaticFieldAccessForField(name, this) != null) ||
99           (_getReferenceTypeForField(name, this) != null);
100       }
101     };
102   }
103
104   /**
105    * Returns whether the given className corresponds to a class
106    * that is anonymous or has an anonymous enclosing class.
107    * @param className the className to check
108    * @return whether the class is anonymous
109    */

110   private boolean hasAnonymous(String JavaDoc className) {
111     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(className, "$");
112     while (st.hasMoreElements()) {
113       String JavaDoc currToken = st.nextToken();
114       try {
115 // Integer anonymousNum =
116
Integer.valueOf(currToken);
117         return true;
118       }
119       catch(NumberFormatException JavaDoc nfe) {
120         // fall through to false because the token cannot be parsed
121
}
122     }
123     return false;
124   }
125
126   /** Returns the fully qualified class name for "this". It will append the package name onto the class name
127    * if there is a package name.
128    */

129   private String JavaDoc _getFullyQualifiedClassNameForThis() {
130     String JavaDoc cName = _thisClassName;
131     if (!_thisPackageName.equals("")) {
132       cName = _thisPackageName + "." + cName;
133     }
134     return cName;
135   }
136
137   private Class JavaDoc<?> _loadClassForThis(Context context) {
138     try {
139       return context.lookupClass(_getFullyQualifiedClassNameForThis());
140     }
141     catch(ClassNotFoundException JavaDoc e) {
142       throw new UnexpectedException(e);
143     }
144   }
145
146   /** Given a field, looks at enclosing classes until it finds one that contains the field. It returns the
147    * ObjectFieldAccess that represents the field.
148    * @param field the name of the field
149    * @param context the context
150    * @return the ObjectFieldAccess that represents the field or null if it cannot find the field in any enclosing
151    * class.
152    */

153   protected ObjectFieldAccess _getObjectFieldAccessForField(String JavaDoc field, Context context) {
154     AbstractTypeChecker tc = makeTypeChecker(context);
155     int numDollars = _getNumDollars(_thisClassName);
156
157     // Check if this has an anonymous inner class
158
if (hasAnonymous(_thisClassName)) {
159       // Get the class
160
Class JavaDoc<?> c = _loadClassForThis(context);
161       Field[] fields = c.getDeclaredFields();
162
163       // Check for a field that begins with this$
164
for (int i = 0; i < fields.length; i++) {
165         if (fields[i].getName().startsWith("this$")) {
166           String JavaDoc fieldName = fields[i].getName();
167           int lastIndex = fieldName.lastIndexOf("$");
168           numDollars = Integer.valueOf(fieldName.substring(lastIndex+1, fieldName.length())).intValue() + 1;
169           break;
170         }
171       }
172     }
173     for (int i = 0; i <= numDollars; i++) {
174       Expression expr = _buildObjectFieldAccess(i, numDollars);
175       Expression newExpr = new ObjectFieldAccess(expr, field);
176       try {
177         // the type checker will tell us if it's a field
178
tc.visit((ObjectFieldAccess) newExpr);
179         return (ObjectFieldAccess) newExpr;
180       }
181       catch (ExecutionError e) {
182         // try concatenating "val$" to the beginning of field
183
newExpr = new ObjectFieldAccess(expr, "val$" + field);
184         try {
185           // the type checker will tell us if it's a field
186
tc.visit((ObjectFieldAccess)newExpr);
187           return (ObjectFieldAccess)newExpr;
188         }
189         catch (ExecutionError e2) {
190           // do nothing, try an outer class
191
}
192       }
193     }
194
195     return null;
196   }
197
198   /**
199    * Given a method, looks at enclosing classes until it finds
200    * one that contains the method. It returns the ObjectMethodCall
201    * that represents the method.
202    * @param method the method
203    * @param context the context
204    * @return the ObjectMethodCall that represents the method or null
205    * if it cannot find the method in any enclosing class.
206    */

207   protected ObjectMethodCall _getObjectMethodCallForFunction(MethodCall method, Context context) {
208     AbstractTypeChecker tc = makeTypeChecker(context);
209     int numDollars = _getNumDollars(_thisClassName);
210     String JavaDoc methodName = method.getMethodName();
211     List JavaDoc<Expression> args = method.getArguments();
212
213     // Check if this has an anonymous inner class
214
if (hasAnonymous(_thisClassName)) {
215       // Get the class
216
Class JavaDoc<?> c = _loadClassForThis(context);
217       Field[] fields = c.getDeclaredFields();
218
219       // Check for a field that begins with this$
220
for (int i = 0; i < fields.length; i++) {
221         if (fields[i].getName().startsWith("this$")) {
222           String JavaDoc fieldName = fields[i].getName();
223           int lastIndex = fieldName.lastIndexOf("$");
224           numDollars = Integer.valueOf(fieldName.substring(lastIndex+1, fieldName.length())).intValue() + 1;
225           break;
226         }
227       }
228     }
229     for (int i = 0; i <= numDollars; i++) {
230       Expression expr = _buildObjectFieldAccess(i, numDollars);
231       expr = new ObjectMethodCall(expr, methodName, args, null, 0, 0, 0, 0);
232       try {
233         // the type checker will tell us if it's a field
234
tc.visit((ObjectMethodCall)expr);
235         return (ObjectMethodCall)expr;
236       }
237       catch (ExecutionError e2) {
238         // do nothing, try an outer class
239
}
240     }
241     return null;
242   }
243
244   /**
245    * Given a field in a static context, looks at enclosing classes until it
246    * finds one that contains the field. It returns the StaticFieldAccess
247    * that represents the field.
248    * @param field the name of the field
249    * @param context the context
250    * @return the StaticFieldAccess that represents the field or null
251    * if it cannot find the field in any enclosing class.
252    */

253   protected StaticFieldAccess _getStaticFieldAccessForField(String JavaDoc field, Context context) {
254     AbstractTypeChecker tc = makeTypeChecker(context);
255     int numDollars = _getNumDollars(_thisClassName);
256     String JavaDoc currClass = _getFullyQualifiedClassNameForThis();
257     int index = currClass.length();
258     // iterate outward trying to find the field
259
for (int i = 0; i <= numDollars; i++) {
260       currClass = currClass.substring(0, index);
261       ReferenceType rt = new ReferenceType(currClass);
262       StaticFieldAccess expr = new StaticFieldAccess(rt, field);
263       try {
264         // the type checker will tell us if it's a field
265
tc.visit(expr);
266         return expr;
267       }
268       catch (ExecutionError e2) {
269         // try an outer class
270
index = currClass.lastIndexOf("$");
271       }
272     }
273     return null;
274   }
275
276   /**
277    * Given a method in a static context, looks at enclosing classes until it
278    * finds one that contains the method. It returns the StaticMethodCall
279    * that represents the method.
280    * @param method the method
281    * @param context the context
282    * @return the StaticMethodCall that represents the method or null
283    * if it cannot find the method in any enclosing class.
284    */

285   protected StaticMethodCall _getStaticMethodCallForFunction(MethodCall method, Context context) {
286     AbstractTypeChecker tc = makeTypeChecker(context);
287     int numDollars = _getNumDollars(_thisClassName);
288     String JavaDoc methodName = method.getMethodName();
289     List JavaDoc<Expression> args = method.getArguments();
290     String JavaDoc currClass = _getFullyQualifiedClassNameForThis();
291     int index = currClass.length();
292     // iterate outward trying to find the method
293
for (int i = 0; i <= numDollars; i++) {
294       currClass = currClass.substring(0, index);
295       ReferenceType rt = new ReferenceType(currClass);
296       StaticMethodCall expr = new StaticMethodCall(rt, methodName, args);
297       try {
298         // the type checker will tell us if it's a field
299
tc.visit(expr);
300         return expr;
301       }
302       catch (ExecutionError e2) {
303         // try an outer class
304
index = currClass.lastIndexOf("$");
305       }
306     }
307     return null;
308   }
309
310   /**
311    * Given the name of an not fully qualified outer class, return the fully qualified
312    * ReferenceType that corresponds to that class. This is called when the user
313    * references a static field of an outer class.
314    * @param field the name of the not fully qualified outer class
315    * @param context the context
316    * @return the ReferenceType that represents the field(in this case,
317    * really a class) or null if it cannot load the corresponding class in the
318    * class loader.
319    */

320   protected ReferenceType _getReferenceTypeForField(String JavaDoc field, Context context) {
321     AbstractTypeChecker tc = makeTypeChecker(context);
322     int index = _indexOfWithinBoundaries(_getFullyQualifiedClassNameForThis(), field);
323     if (index != -1) {
324       // field may be of form outerClass$innerClass or
325
// package.innerClass.
326
// We want to match the inner most class.
327
int lastDollar = field.lastIndexOf("$");
328       int lastDot = field.lastIndexOf(".");
329       if (lastDollar != -1) {
330         field = field.substring(lastDollar + 1, field.length());
331       }
332       else {
333         if (lastDot != -1) {
334           field = field.substring(lastDot + 1, field.length());
335         }
336       }
337       LinkedList JavaDoc<IdentifierToken> list = new LinkedList JavaDoc<IdentifierToken>();
338       StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(_getFullyQualifiedClassNameForThis(), "$.");
339       String JavaDoc currString = st.nextToken();
340       while (!currString.equals(field)) {
341         list.add(new Identifier(currString));
342         currString = st.nextToken();
343       }
344       list.add(new Identifier(field));
345       ReferenceType rt = new ReferenceType(list);
346       try {
347         // the type checker will tell us if it's a class
348
tc.visit(rt);
349         return rt;
350       }
351       catch (ExecutionError e) {
352         return null;
353       }
354     }
355     else {
356       return null;
357     }
358   }
359
360
361   /** Sets the class name of "this", parsing out the package name. */
362   protected void setClassName(String JavaDoc className) {
363     int indexLastDot = className.lastIndexOf(".");
364     if (indexLastDot == -1) _thisPackageName = "";
365     else _thisPackageName = className.substring(0,indexLastDot);
366     _thisClassName = className.substring(indexLastDot + 1);
367   }
368
369   /** Helper method to convert a ThisExpression to a QualifiedName. Allows us to redefine "this" in a debug interpreter.
370    * @param node ThisExpression
371    * @return corresponding QualifiedName
372    */

373   protected QualifiedName _convertThisToName(ThisExpression node) {
374     // Can't parametize this List for some reason.
375
List JavaDoc<IdentifierToken> ids = new LinkedList JavaDoc<IdentifierToken>(); // Add parameterization <Identifier>.
376
ids.add(new Identifier("this", node.getBeginLine(), node.getBeginColumn(),
377                            node.getEndLine(), node.getEndColumn()));
378     return new QualifiedName(ids, node.getFilename(), // TODO: change getFilename to GetFileName in DynamicJava
379
node.getBeginLine(), node.getBeginColumn(),
380                              node.getEndLine(), node.getEndColumn());
381   }
382
383   /**
384    * Helper method to convert a ThisExpression to a FieldAccess.
385    * Allows us to access fields of outer classes in a debug interpreter.
386    * @param node ThisExpression
387    * @return corresponding FieldAccess
388    */

389   protected Expression _convertThisToObjectFieldAccess(ThisExpression node) {
390     String JavaDoc className = node.getClassName();
391     int numToWalk = verifyClassName(className);
392     int numDollars = _getNumDollars(_thisClassName);
393     // if numToWalk == 0, just return "this"
394
if (numToWalk == -1) {
395       throw new ExecutionError("malformed.expression", node);
396     }
397     else {
398       return _buildObjectFieldAccess(numToWalk, numDollars);
399     }
400   }
401
402   /**
403    * Builds a ThisExpression that has no class name.
404    * @return an unqualified ThisExpression
405    */

406   protected ThisExpression buildUnqualifiedThis() {
407     LinkedList JavaDoc<IdentifierToken> ids = new LinkedList JavaDoc<IdentifierToken>();
408     return new ThisExpression(ids, "", 0, 0, 0, 0);
409   }
410
411   /**
412    * Helper method to build an ObjectFieldAccess for a ThisExpression
413    * given the number of classes to walk and the number of dollars.
414    * @param numToWalk number of outer classes to walk through
415    * @param numDollars numer of dollars in _thisClassName
416    * @return a QualifiedName is numtoWalk is zero or an ObjectFieldAccess
417    */

418   private Expression _buildObjectFieldAccess(int numToWalk, int numDollars) {
419     if (numToWalk == 0) {
420       return _convertThisToName(buildUnqualifiedThis());
421     }
422     else {
423       return new ObjectFieldAccess(_buildObjectFieldAccess(numToWalk - 1, numDollars), "this$" + (numDollars - numToWalk));
424     }
425   }
426
427   /**
428    * Returns the index of subString within string if the substring is
429    * either bounded by the ends of string or by $'s.
430    * @param string the super string
431    * @param subString the subString
432    * @return the index of string that subString begins at or -1
433    * if subString is not in string or is not validly bounded
434    */

435   private int _indexOfWithinBoundaries(String JavaDoc string, String JavaDoc subString) {
436     int index = string.indexOf(subString);
437     if (index == -1) {
438       return index;
439     }
440     // subString is somewhere in string
441
else {
442       // ends at legal boundary
443
if (((string.length() == subString.length() + index) ||
444            (string.charAt(subString.length() + index) == '$'))
445             &&
446           // begins at legal boundary
447
((index == 0) ||
448            (string.charAt(index-1) == '$') ||
449            (string.charAt(index-1) == '.'))) {
450         return index;
451       }
452       else {
453         return -1;
454       }
455     }
456   }
457
458   /**
459    * Returns the number of dollar characters in
460    * a given String.
461    * @param className the string to be examined
462    * @return the number of dollars in the string
463    */

464   private int _getNumDollars(String JavaDoc className) {
465     int numDollars = 0;
466     int index = className.indexOf("$");
467     while (index != -1) {
468       numDollars++;
469       index = className.indexOf("$", index + 1);
470     }
471     return numDollars;
472   }
473
474   /**
475    * Checks if the className passed in is a valid className.
476    * @param className the className of the ThisExpression
477    * @return the number of outer classes to walk out to
478    */

479   protected int verifyClassName(String JavaDoc className) {
480     boolean hasPackage = false;
481     if (!_thisPackageName.equals("")) {
482       int index = className.indexOf(_thisPackageName);
483       if (index == 0) {
484         hasPackage = true;
485         // className begins with the package name
486
index = _thisPackageName.length() + 1;
487         if (index >= className.length()) {
488           return -1;
489         }
490         // strip off the package
491
className = className.substring(index, className.length());
492       }
493     }
494
495     className = className.replace('.', '$');
496     int indexWithBoundaries = _indexOfWithinBoundaries(_thisClassName, className);
497     if ((hasPackage && indexWithBoundaries != 0) ||
498         (indexWithBoundaries == -1)) {
499       return -1;
500     }
501     else {
502       return _getNumDollars(_thisClassName.substring(indexWithBoundaries + className.length()));
503     }
504   }
505
506   /**
507    * Converts the ThisExpression to a QualifiedName
508    * if it has no class name or an ObjectFieldAccess
509    * if it does.
510    * @param node the expression to visit
511    * @return the converted form of the node
512    */

513   protected Expression visitThis(ThisExpression node) {
514     if (node.getClassName().equals("")) {
515       return _convertThisToName(node);
516     }
517     else {
518       return _convertThisToObjectFieldAccess(node);
519     }
520   }
521
522   /**
523    * Makes an anonymous IdentityVisitor that overrides
524    * visit for a ThisExpresssion to convert it to
525    * either a QualifiedName or an ObjectFieldAccess
526    */

527   public IdentityVisitor makeTranslationVisitor() {
528     return new IdentityVisitor() {
529       public Node visit(ThisExpression node) {
530         Expression e = visitThis(node);
531         if (e instanceof QualifiedName) {
532           return visit((QualifiedName)e);
533         }
534         else if (e instanceof ObjectFieldAccess) {
535           return visit((ObjectFieldAccess)e);
536         }
537         else {
538           throw new UnexpectedException(new IllegalArgumentException JavaDoc("Illegal type of Expression"));
539         }
540       }
541     };
542   }
543
544 // private Class _getClassForType(Type type, Context context) {
545
// Class c = (ClassProperty(NodeProperties.TYPE);
546
// if (c != null) {
547
// return c;
548
// }
549
// else if (type instanceof PrimitiveType) {
550
// return ((PrimitiveType)type).getValue();
551
// }
552
// else if (type instanceof ReferenceType) {
553
// ReferenceType rt = (ReferenceType) type;
554
// try {
555
// return context.lookupClass(rt.getRepresentation());
556
// }
557
// catch (ClassNotFoundException e) {
558
// rt.setProperty(NodeProperties.ERROR_STRINGS,
559
// new String[] { rt.getRepresentation() });
560
// throw new ExecutionError("undefined.class", rt);
561
// }
562
// }
563
// else {
564
// // type should be an ArrayType
565
// Type eType = ((ArrayType)type).getElementType();
566
// c = _getClassForType(eType, context);
567
// return java.lang.reflect.Array.newInstance(c, 0).getClass();
568
// }
569
// }
570

571   /**
572    * Factory method to make a new NameChecker that tries to find the
573    * right scope for QualifiedNames.
574    * @param nameContext Context for the NameVisitor
575    * @return the visitor
576    */

577   public NameVisitor makeNameVisitor(final Context nameContext) {
578     return new NameVisitor(nameContext) {
579       // try {
580
// return super.visit(node);
581
// }
582
// catch (ExecutionError e) {
583
// System.out.println(e.getMessage());
584
// if (e.getMessage().startsWith("Redefinition of")) {
585
// Type type = node.getType();
586
// String name = node.getName();
587
// Class oldClass = (Class)nameContext.get(name);
588
// Class newClass = _getClassForType(type, nameContext);
589
// if (oldClass.equals(newClass)) {
590
// // This is a redefinition of the same variable
591
// // with the same type. Allow the user to do
592
// // this so make a new SimpleAssignExpression
593
// Identifier id = new Identifier(name);
594
// LinkedList ids = new LinkedList();
595
// ids.add(id);
596
// QualifiedName qName = new QualifiedName(ids);
597
// return new SimpleAssignExpression(qName, node.getInitializer());
598
// }
599
// }
600
// throw e;
601
// }
602
// }
603
public Node visit(QualifiedName node) {
604         try {
605           return super.visit(node);
606         }
607         catch(ExecutionError e) {
608           // This error is thrown only if this QualifiedName is not
609
// a local variable or a class
610
final List JavaDoc<IdentifierToken> ids = node.getIdentifiers();
611           final Iterator JavaDoc<IdentifierToken> iter = ids.iterator();
612           final StringBuilder JavaDoc fieldBuf = new StringBuilder JavaDoc(iter.next().image());
613           while (iter.hasNext()) {
614             IdentifierToken t = iter.next();
615             fieldBuf.append('$').append(t.image());
616           }
617           String JavaDoc field = fieldBuf.toString();
618           if (nameContext.isDefined("this")) {
619             // Check if it's a field or outer field if we're not in a
620
// static context.
621
ObjectFieldAccess ofa = _getObjectFieldAccessForField(field, nameContext);
622             if (ofa != null) return ofa;
623           }
624           else {
625             // We're in a static context.
626
StaticFieldAccess sfa = _getStaticFieldAccessForField(field, nameContext);
627             if (sfa != null) return sfa;
628             else {
629               // This is possibly a substring of our current context's class name.
630
// (e.g. The user is trying to evaluate MonkeyOuter.someField and we
631
// have to convert MonkeyOuter to MonkeyMostOuter$MonkeyOuter)
632
// Try qualifying it.
633
ReferenceType rt = _getReferenceTypeForField(field, nameContext);
634               if (rt != null) return rt;
635             }
636           }
637           // Didn't find this field in any outer class. Throw original exception.
638
throw e;
639         }
640       }
641       public Node visit(ObjectMethodCall node) {
642         MethodCall method = (MethodCall) super.visit(node);
643         // if (method != null) this object method call is either a method with no
644
// class before it or is a static method call
645
if (method != null) {
646           if (method instanceof StaticMethodCall) {
647             return method;
648           }
649           // now we know that method is a FunctionCall
650
else if (nameContext.isDefined("this")) {
651             ObjectMethodCall omc = _getObjectMethodCallForFunction(method, nameContext);
652             if (omc != null) {
653               return omc;
654             }
655             else {
656               return method;
657             }
658           }
659           // it's a FunctionCall from a static context
660
else {
661             StaticMethodCall smc = _getStaticMethodCallForFunction(method, nameContext);
662             if (smc != null) {
663               return smc;
664             }
665             else {
666               return method;
667             }
668           }
669         }
670         else {
671           return null;
672         }
673       }
674     };
675   }
676
677   /**
678    * Factory method to make a new TypeChecker that treats "this" as a variable.
679    * @param context the context
680    * @return visitor the visitor
681    */

682   public AbstractTypeChecker makeTypeChecker(final Context context) {
683     if (Float.valueOf(System.getProperty("java.specification.version")) < 1.5) {
684       return new TypeChecker14(context) {
685       /**
686        * Visits a QualifiedName, returning our class if it is "this"
687        * @param node the node to visit
688        */

689       public Class JavaDoc<?> visit(QualifiedName node) {
690         String JavaDoc var = node.getRepresentation();
691         if ("this".equals(var)) {
692           // String cName = _thisClassName.replace('$', '.');
693
// if (!_thisPackageName.equals("")) {
694
// cName = _thisPackageName + "." + cName;
695
// }
696
// Class c = context.lookupClass(cName);
697
Class JavaDoc<?> c = _loadClassForThis(context);
698           node.setProperty(NodeProperties.TYPE, c);
699           node.setProperty(NodeProperties.MODIFIER, context.getModifier(node));
700           return c;
701         }
702         else return super.visit(node);
703       }
704
705       };
706     }
707     else {
708       return new TypeChecker15(context) {
709         /**
710        * Visits a QualifiedName, returning our class if it is "this"
711        * @param node the node to visit
712        */

713       public Class JavaDoc<?> visit(QualifiedName node) {
714         String JavaDoc var = node.getRepresentation();
715         if ("this".equals(var)) {
716           // String cName = _thisClassName.replace('$', '.');
717
// if (!_thisPackageName.equals("")) {
718
// cName = _thisPackageName + "." + cName;
719
// }
720
// Class c = context.lookupClass(cName);
721
Class JavaDoc<?> c = _loadClassForThis(context);
722           node.setProperty(NodeProperties.TYPE, c);
723           node.setProperty(NodeProperties.MODIFIER, context.getModifier(node));
724           return c;
725         }
726         else return super.visit(node);
727       }
728
729       };
730     }
731   }
732       
733 }
734
Popular Tags