001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.model.repl;
038:
039: import koala.dynamicjava.interpreter.error.*;
040: import koala.dynamicjava.interpreter.*;
041: import koala.dynamicjava.interpreter.context.*;
042: import koala.dynamicjava.tree.*;
043:
044: import java.util.List;
045: import java.util.LinkedList;
046: import java.util.Iterator;
047: import java.util.StringTokenizer;
048: import java.lang.reflect.*;
049:
050: import edu.rice.cs.drjava.model.repl.newjvm.ClassPathManager;
051: import edu.rice.cs.util.UnexpectedException;
052:
053: /** This class is an extension to DynamicJavaAdapter that allows us to process expressions involving the "this"
054: * keyword correctly in the current debug interpreter context. This allows users to debug outer classes and their
055: * fields using the usual Java syntax of outerclass.this. This is done by holding on to the class name of "this"
056: * and by translating references to outer instance classes to field accesses in the form "this.this$N.this$N-1...".
057: *
058: * This class is loaded in the Interpreter JVM, not the Main JVM.
059: * (Do not use DrJava's config framework here.)
060: *
061: * @version $Id: JavaDebugInterpreter.java 4255 2007-08-28 19:17:37Z mgricken $
062: */
063: public class JavaDebugInterpreter extends DynamicJavaAdapter {
064:
065: /** This interpreter's name. */
066: protected final String _name;
067:
068: /** The class name of the "this" object for the currently suspended thread. */
069: protected String _this ClassName;
070:
071: /** The name of the package containing _this, if any. */
072: protected String _this PackageName;
073:
074: /** Extends IdentityVisitor to convert all instances of ThisExpressions in the tree to either QualifiedName or an
075: * ObjectFieldAccess
076: */
077: protected IdentityVisitor _translationVisitor;
078:
079: /** Creates a new debug interpreter.
080: * @param name the name of the interpreter
081: * @param className the class name of the current context of "this"
082: */
083: public JavaDebugInterpreter(String name, String className) {
084: super (new ClassPathManager());
085: _name = name;
086: setClassName(className);
087: _translationVisitor = makeTranslationVisitor();
088: }
089:
090: /** Processes the tree before evaluating it. The translation visitor visits each node in the tree for the given
091: * statement or expression and converts the necessary nodes.
092: * @param node Tree to process
093: */
094: public Node processTree(Node node) {
095: return node.acceptVisitor(_translationVisitor);
096: }
097:
098: public GlobalContext makeGlobalContext(TreeInterpreter i) {
099: return new GlobalContext(i) {
100: public boolean exists(String name) {
101: return (super .exists(name))
102: || (_getObjectFieldAccessForField(name, this ) != null)
103: || (_getStaticFieldAccessForField(name, this ) != null)
104: || (_getReferenceTypeForField(name, this ) != null);
105: }
106: };
107: }
108:
109: /**
110: * Returns whether the given className corresponds to a class
111: * that is anonymous or has an anonymous enclosing class.
112: * @param className the className to check
113: * @return whether the class is anonymous
114: */
115: private boolean hasAnonymous(String className) {
116: StringTokenizer st = new StringTokenizer(className, "$");
117: while (st.hasMoreElements()) {
118: String currToken = st.nextToken();
119: try {
120: // Integer anonymousNum =
121: Integer.valueOf(currToken);
122: return true;
123: } catch (NumberFormatException nfe) {
124: // fall through to false because the token cannot be parsed
125: }
126: }
127: return false;
128: }
129:
130: /** Returns the fully qualified class name for "this". It will append the package name onto the class name
131: * if there is a package name.
132: */
133: private String _getFullyQualifiedClassNameForThis() {
134: String cName = _this ClassName;
135: if (!_this PackageName.equals("")) {
136: cName = _this PackageName + "." + cName;
137: }
138: return cName;
139: }
140:
141: private Class<?> _loadClassForThis(Context context) {
142: try {
143: return context
144: .lookupClass(_getFullyQualifiedClassNameForThis());
145: } catch (ClassNotFoundException e) {
146: throw new UnexpectedException(e);
147: }
148: }
149:
150: /** Given a field, looks at enclosing classes until it finds one that contains the field. It returns the
151: * ObjectFieldAccess that represents the field.
152: * @param field the name of the field
153: * @param context the context
154: * @return the ObjectFieldAccess that represents the field or null if it cannot find the field in any enclosing
155: * class.
156: */
157: protected ObjectFieldAccess _getObjectFieldAccessForField(
158: String field, Context context) {
159: AbstractTypeChecker tc = makeTypeChecker(context);
160: int numDollars = _getNumDollars(_this ClassName);
161:
162: // Check if this has an anonymous inner class
163: if (hasAnonymous(_this ClassName)) {
164: // Get the class
165: Class<?> c = _loadClassForThis(context);
166: Field[] fields = c.getDeclaredFields();
167:
168: // Check for a field that begins with this$
169: for (int i = 0; i < fields.length; i++) {
170: if (fields[i].getName().startsWith("this$")) {
171: String fieldName = fields[i].getName();
172: int lastIndex = fieldName.lastIndexOf("$");
173: numDollars = Integer.valueOf(
174: fieldName.substring(lastIndex + 1,
175: fieldName.length())).intValue() + 1;
176: break;
177: }
178: }
179: }
180: for (int i = 0; i <= numDollars; i++) {
181: Expression expr = _buildObjectFieldAccess(i, numDollars);
182: Expression newExpr = new ObjectFieldAccess(expr, field);
183: try {
184: // the type checker will tell us if it's a field
185: tc.visit((ObjectFieldAccess) newExpr);
186: return (ObjectFieldAccess) newExpr;
187: } catch (ExecutionError e) {
188: // try concatenating "val$" to the beginning of field
189: newExpr = new ObjectFieldAccess(expr, "val$" + field);
190: try {
191: // the type checker will tell us if it's a field
192: tc.visit((ObjectFieldAccess) newExpr);
193: return (ObjectFieldAccess) newExpr;
194: } catch (ExecutionError e2) {
195: // do nothing, try an outer class
196: }
197: }
198: }
199:
200: return null;
201: }
202:
203: /**
204: * Given a method, looks at enclosing classes until it finds
205: * one that contains the method. It returns the ObjectMethodCall
206: * that represents the method.
207: * @param method the method
208: * @param context the context
209: * @return the ObjectMethodCall that represents the method or null
210: * if it cannot find the method in any enclosing class.
211: */
212: protected ObjectMethodCall _getObjectMethodCallForFunction(
213: MethodCall method, Context context) {
214: AbstractTypeChecker tc = makeTypeChecker(context);
215: int numDollars = _getNumDollars(_this ClassName);
216: String methodName = method.getMethodName();
217: List<Expression> args = method.getArguments();
218:
219: // Check if this has an anonymous inner class
220: if (hasAnonymous(_this ClassName)) {
221: // Get the class
222: Class<?> c = _loadClassForThis(context);
223: Field[] fields = c.getDeclaredFields();
224:
225: // Check for a field that begins with this$
226: for (int i = 0; i < fields.length; i++) {
227: if (fields[i].getName().startsWith("this$")) {
228: String fieldName = fields[i].getName();
229: int lastIndex = fieldName.lastIndexOf("$");
230: numDollars = Integer.valueOf(
231: fieldName.substring(lastIndex + 1,
232: fieldName.length())).intValue() + 1;
233: break;
234: }
235: }
236: }
237: for (int i = 0; i <= numDollars; i++) {
238: Expression expr = _buildObjectFieldAccess(i, numDollars);
239: expr = new ObjectMethodCall(expr, methodName, args, null,
240: 0, 0, 0, 0);
241: try {
242: // the type checker will tell us if it's a field
243: tc.visit((ObjectMethodCall) expr);
244: return (ObjectMethodCall) expr;
245: } catch (ExecutionError e2) {
246: // do nothing, try an outer class
247: }
248: }
249: return null;
250: }
251:
252: /**
253: * Given a field in a static context, looks at enclosing classes until it
254: * finds one that contains the field. It returns the StaticFieldAccess
255: * that represents the field.
256: * @param field the name of the field
257: * @param context the context
258: * @return the StaticFieldAccess that represents the field or null
259: * if it cannot find the field in any enclosing class.
260: */
261: protected StaticFieldAccess _getStaticFieldAccessForField(
262: String field, Context context) {
263: AbstractTypeChecker tc = makeTypeChecker(context);
264: int numDollars = _getNumDollars(_this ClassName);
265: String currClass = _getFullyQualifiedClassNameForThis();
266: int index = currClass.length();
267: // iterate outward trying to find the field
268: for (int i = 0; i <= numDollars; i++) {
269: currClass = currClass.substring(0, index);
270: ReferenceType rt = new ReferenceType(currClass);
271: StaticFieldAccess expr = new StaticFieldAccess(rt, field);
272: try {
273: // the type checker will tell us if it's a field
274: tc.visit(expr);
275: return expr;
276: } catch (ExecutionError e2) {
277: // try an outer class
278: index = currClass.lastIndexOf("$");
279: }
280: }
281: return null;
282: }
283:
284: /**
285: * Given a method in a static context, looks at enclosing classes until it
286: * finds one that contains the method. It returns the StaticMethodCall
287: * that represents the method.
288: * @param method the method
289: * @param context the context
290: * @return the StaticMethodCall that represents the method or null
291: * if it cannot find the method in any enclosing class.
292: */
293: protected StaticMethodCall _getStaticMethodCallForFunction(
294: MethodCall method, Context context) {
295: AbstractTypeChecker tc = makeTypeChecker(context);
296: int numDollars = _getNumDollars(_this ClassName);
297: String methodName = method.getMethodName();
298: List<Expression> args = method.getArguments();
299: String currClass = _getFullyQualifiedClassNameForThis();
300: int index = currClass.length();
301: // iterate outward trying to find the method
302: for (int i = 0; i <= numDollars; i++) {
303: currClass = currClass.substring(0, index);
304: ReferenceType rt = new ReferenceType(currClass);
305: StaticMethodCall expr = new StaticMethodCall(rt,
306: methodName, args);
307: try {
308: // the type checker will tell us if it's a field
309: tc.visit(expr);
310: return expr;
311: } catch (ExecutionError e2) {
312: // try an outer class
313: index = currClass.lastIndexOf("$");
314: }
315: }
316: return null;
317: }
318:
319: /**
320: * Given the name of an not fully qualified outer class, return the fully qualified
321: * ReferenceType that corresponds to that class. This is called when the user
322: * references a static field of an outer class.
323: * @param field the name of the not fully qualified outer class
324: * @param context the context
325: * @return the ReferenceType that represents the field(in this case,
326: * really a class) or null if it cannot load the corresponding class in the
327: * class loader.
328: */
329: protected ReferenceType _getReferenceTypeForField(String field,
330: Context context) {
331: AbstractTypeChecker tc = makeTypeChecker(context);
332: int index = _indexOfWithinBoundaries(
333: _getFullyQualifiedClassNameForThis(), field);
334: if (index != -1) {
335: // field may be of form outerClass$innerClass or
336: // package.innerClass.
337: // We want to match the inner most class.
338: int lastDollar = field.lastIndexOf("$");
339: int lastDot = field.lastIndexOf(".");
340: if (lastDollar != -1) {
341: field = field.substring(lastDollar + 1, field.length());
342: } else {
343: if (lastDot != -1) {
344: field = field
345: .substring(lastDot + 1, field.length());
346: }
347: }
348: LinkedList<IdentifierToken> list = new LinkedList<IdentifierToken>();
349: StringTokenizer st = new StringTokenizer(
350: _getFullyQualifiedClassNameForThis(), "$.");
351: String currString = st.nextToken();
352: while (!currString.equals(field)) {
353: list.add(new Identifier(currString));
354: currString = st.nextToken();
355: }
356: list.add(new Identifier(field));
357: ReferenceType rt = new ReferenceType(list);
358: try {
359: // the type checker will tell us if it's a class
360: tc.visit(rt);
361: return rt;
362: } catch (ExecutionError e) {
363: return null;
364: }
365: } else {
366: return null;
367: }
368: }
369:
370: /** Sets the class name of "this", parsing out the package name. */
371: protected void setClassName(String className) {
372: int indexLastDot = className.lastIndexOf(".");
373: if (indexLastDot == -1)
374: _this PackageName = "";
375: else
376: _this PackageName = className.substring(0, indexLastDot);
377: _this ClassName = className.substring(indexLastDot + 1);
378: }
379:
380: /** Helper method to convert a ThisExpression to a QualifiedName. Allows us to redefine "this" in a debug interpreter.
381: * @param node ThisExpression
382: * @return corresponding QualifiedName
383: */
384: protected QualifiedName _convertThisToName(ThisExpression node) {
385: // Can't parametize this List for some reason.
386: List<IdentifierToken> ids = new LinkedList<IdentifierToken>(); // Add parameterization <Identifier>.
387: ids.add(new Identifier("this", node.getBeginLine(), node
388: .getBeginColumn(), node.getEndLine(), node
389: .getEndColumn()));
390: return new QualifiedName(ids, node.getFilename(), // TODO: change getFilename to GetFileName in DynamicJava
391: node.getBeginLine(), node.getBeginColumn(), node
392: .getEndLine(), node.getEndColumn());
393: }
394:
395: /**
396: * Helper method to convert a ThisExpression to a FieldAccess.
397: * Allows us to access fields of outer classes in a debug interpreter.
398: * @param node ThisExpression
399: * @return corresponding FieldAccess
400: */
401: protected Expression _convertThisToObjectFieldAccess(
402: ThisExpression node) {
403: String className = node.getClassName();
404: int numToWalk = verifyClassName(className);
405: int numDollars = _getNumDollars(_this ClassName);
406: // if numToWalk == 0, just return "this"
407: if (numToWalk == -1) {
408: throw new ExecutionError("malformed.expression", node);
409: } else {
410: return _buildObjectFieldAccess(numToWalk, numDollars);
411: }
412: }
413:
414: /**
415: * Builds a ThisExpression that has no class name.
416: * @return an unqualified ThisExpression
417: */
418: protected ThisExpression buildUnqualifiedThis() {
419: LinkedList<IdentifierToken> ids = new LinkedList<IdentifierToken>();
420: return new ThisExpression(ids, "", 0, 0, 0, 0);
421: }
422:
423: /**
424: * Helper method to build an ObjectFieldAccess for a ThisExpression
425: * given the number of classes to walk and the number of dollars.
426: * @param numToWalk number of outer classes to walk through
427: * @param numDollars numer of dollars in _thisClassName
428: * @return a QualifiedName is numtoWalk is zero or an ObjectFieldAccess
429: */
430: private Expression _buildObjectFieldAccess(int numToWalk,
431: int numDollars) {
432: if (numToWalk == 0) {
433: return _convertThisToName(buildUnqualifiedThis());
434: } else {
435: return new ObjectFieldAccess(_buildObjectFieldAccess(
436: numToWalk - 1, numDollars), "this$"
437: + (numDollars - numToWalk));
438: }
439: }
440:
441: /**
442: * Returns the index of subString within string if the substring is
443: * either bounded by the ends of string or by $'s.
444: * @param string the super string
445: * @param subString the subString
446: * @return the index of string that subString begins at or -1
447: * if subString is not in string or is not validly bounded
448: */
449: private int _indexOfWithinBoundaries(String string, String subString) {
450: int index = string.indexOf(subString);
451: if (index == -1) {
452: return index;
453: }
454: // subString is somewhere in string
455: else {
456: // ends at legal boundary
457: if (((string.length() == subString.length() + index) || (string
458: .charAt(subString.length() + index) == '$'))
459: &&
460: // begins at legal boundary
461: ((index == 0) || (string.charAt(index - 1) == '$') || (string
462: .charAt(index - 1) == '.'))) {
463: return index;
464: } else {
465: return -1;
466: }
467: }
468: }
469:
470: /**
471: * Returns the number of dollar characters in
472: * a given String.
473: * @param className the string to be examined
474: * @return the number of dollars in the string
475: */
476: private int _getNumDollars(String className) {
477: int numDollars = 0;
478: int index = className.indexOf("$");
479: while (index != -1) {
480: numDollars++;
481: index = className.indexOf("$", index + 1);
482: }
483: return numDollars;
484: }
485:
486: /**
487: * Checks if the className passed in is a valid className.
488: * @param className the className of the ThisExpression
489: * @return the number of outer classes to walk out to
490: */
491: protected int verifyClassName(String className) {
492: boolean hasPackage = false;
493: if (!_this PackageName.equals("")) {
494: int index = className.indexOf(_this PackageName);
495: if (index == 0) {
496: hasPackage = true;
497: // className begins with the package name
498: index = _this PackageName.length() + 1;
499: if (index >= className.length()) {
500: return -1;
501: }
502: // strip off the package
503: className = className.substring(index, className
504: .length());
505: }
506: }
507:
508: className = className.replace('.', '$');
509: int indexWithBoundaries = _indexOfWithinBoundaries(
510: _this ClassName, className);
511: if ((hasPackage && indexWithBoundaries != 0)
512: || (indexWithBoundaries == -1)) {
513: return -1;
514: } else {
515: return _getNumDollars(_this ClassName
516: .substring(indexWithBoundaries + className.length()));
517: }
518: }
519:
520: /**
521: * Converts the ThisExpression to a QualifiedName
522: * if it has no class name or an ObjectFieldAccess
523: * if it does.
524: * @param node the expression to visit
525: * @return the converted form of the node
526: */
527: protected Expression visitThis(ThisExpression node) {
528: if (node.getClassName().equals("")) {
529: return _convertThisToName(node);
530: } else {
531: return _convertThisToObjectFieldAccess(node);
532: }
533: }
534:
535: /**
536: * Makes an anonymous IdentityVisitor that overrides
537: * visit for a ThisExpresssion to convert it to
538: * either a QualifiedName or an ObjectFieldAccess
539: */
540: public IdentityVisitor makeTranslationVisitor() {
541: return new IdentityVisitor() {
542: public Node visit(ThisExpression node) {
543: Expression e = visitThis(node);
544: if (e instanceof QualifiedName) {
545: return visit((QualifiedName) e);
546: } else if (e instanceof ObjectFieldAccess) {
547: return visit((ObjectFieldAccess) e);
548: } else {
549: throw new UnexpectedException(
550: new IllegalArgumentException(
551: "Illegal type of Expression"));
552: }
553: }
554: };
555: }
556:
557: // private Class _getClassForType(Type type, Context context) {
558: // Class c = (ClassProperty(NodeProperties.TYPE);
559: // if (c != null) {
560: // return c;
561: // }
562: // else if (type instanceof PrimitiveType) {
563: // return ((PrimitiveType)type).getValue();
564: // }
565: // else if (type instanceof ReferenceType) {
566: // ReferenceType rt = (ReferenceType) type;
567: // try {
568: // return context.lookupClass(rt.getRepresentation());
569: // }
570: // catch (ClassNotFoundException e) {
571: // rt.setProperty(NodeProperties.ERROR_STRINGS,
572: // new String[] { rt.getRepresentation() });
573: // throw new ExecutionError("undefined.class", rt);
574: // }
575: // }
576: // else {
577: // // type should be an ArrayType
578: // Type eType = ((ArrayType)type).getElementType();
579: // c = _getClassForType(eType, context);
580: // return java.lang.reflect.Array.newInstance(c, 0).getClass();
581: // }
582: // }
583:
584: /**
585: * Factory method to make a new NameChecker that tries to find the
586: * right scope for QualifiedNames.
587: * @param nameContext Context for the NameVisitor
588: * @return the visitor
589: */
590: public NameVisitor makeNameVisitor(final Context nameContext) {
591: return new NameVisitor(nameContext) {
592: // try {
593: // return super.visit(node);
594: // }
595: // catch (ExecutionError e) {
596: // System.out.println(e.getMessage());
597: // if (e.getMessage().startsWith("Redefinition of")) {
598: // Type type = node.getType();
599: // String name = node.getName();
600: // Class oldClass = (Class)nameContext.get(name);
601: // Class newClass = _getClassForType(type, nameContext);
602: // if (oldClass.equals(newClass)) {
603: // // This is a redefinition of the same variable
604: // // with the same type. Allow the user to do
605: // // this so make a new SimpleAssignExpression
606: // Identifier id = new Identifier(name);
607: // LinkedList ids = new LinkedList();
608: // ids.add(id);
609: // QualifiedName qName = new QualifiedName(ids);
610: // return new SimpleAssignExpression(qName, node.getInitializer());
611: // }
612: // }
613: // throw e;
614: // }
615: // }
616: public Node visit(QualifiedName node) {
617: try {
618: return super .visit(node);
619: } catch (ExecutionError e) {
620: // This error is thrown only if this QualifiedName is not
621: // a local variable or a class
622: final List<IdentifierToken> ids = node
623: .getIdentifiers();
624: final Iterator<IdentifierToken> iter = ids
625: .iterator();
626: final StringBuilder fieldBuf = new StringBuilder(
627: iter.next().image());
628: while (iter.hasNext()) {
629: IdentifierToken t = iter.next();
630: fieldBuf.append('$').append(t.image());
631: }
632: String field = fieldBuf.toString();
633: if (nameContext.isDefined("this")) {
634: // Check if it's a field or outer field if we're not in a
635: // static context.
636: ObjectFieldAccess ofa = _getObjectFieldAccessForField(
637: field, nameContext);
638: if (ofa != null)
639: return ofa;
640: } else {
641: // We're in a static context.
642: StaticFieldAccess sfa = _getStaticFieldAccessForField(
643: field, nameContext);
644: if (sfa != null)
645: return sfa;
646: else {
647: // This is possibly a substring of our current context's class name.
648: // (e.g. The user is trying to evaluate MonkeyOuter.someField and we
649: // have to convert MonkeyOuter to MonkeyMostOuter$MonkeyOuter)
650: // Try qualifying it.
651: ReferenceType rt = _getReferenceTypeForField(
652: field, nameContext);
653: if (rt != null)
654: return rt;
655: }
656: }
657: // Didn't find this field in any outer class. Throw original exception.
658: throw e;
659: }
660: }
661:
662: public Node visit(ObjectMethodCall node) {
663: MethodCall method = (MethodCall) super .visit(node);
664: // if (method != null) this object method call is either a method with no
665: // class before it or is a static method call
666: if (method != null) {
667: if (method instanceof StaticMethodCall) {
668: return method;
669: }
670: // now we know that method is a FunctionCall
671: else if (nameContext.isDefined("this")) {
672: ObjectMethodCall omc = _getObjectMethodCallForFunction(
673: method, nameContext);
674: if (omc != null) {
675: return omc;
676: } else {
677: return method;
678: }
679: }
680: // it's a FunctionCall from a static context
681: else {
682: StaticMethodCall smc = _getStaticMethodCallForFunction(
683: method, nameContext);
684: if (smc != null) {
685: return smc;
686: } else {
687: return method;
688: }
689: }
690: } else {
691: return null;
692: }
693: }
694: };
695: }
696:
697: /**
698: * Factory method to make a new TypeChecker that treats "this" as a variable.
699: * @param context the context
700: * @return visitor the visitor
701: */
702: public AbstractTypeChecker makeTypeChecker(final Context context) {
703: if (Float.valueOf(System
704: .getProperty("java.specification.version")) < 1.5) {
705: return new TypeChecker14(context) {
706: /**
707: * Visits a QualifiedName, returning our class if it is "this"
708: * @param node the node to visit
709: */
710: public Class<?> visit(QualifiedName node) {
711: String var = node.getRepresentation();
712: if ("this".equals(var)) {
713: // String cName = _thisClassName.replace('$', '.');
714: // if (!_thisPackageName.equals("")) {
715: // cName = _thisPackageName + "." + cName;
716: // }
717: // Class c = context.lookupClass(cName);
718: Class<?> c = _loadClassForThis(context);
719: node.setProperty(NodeProperties.TYPE, c);
720: node.setProperty(NodeProperties.MODIFIER,
721: context.getModifier(node));
722: return c;
723: } else
724: return super .visit(node);
725: }
726:
727: };
728: } else {
729: return new TypeChecker15(context) {
730: /**
731: * Visits a QualifiedName, returning our class if it is "this"
732: * @param node the node to visit
733: */
734: public Class<?> visit(QualifiedName node) {
735: String var = node.getRepresentation();
736: if ("this".equals(var)) {
737: // String cName = _thisClassName.replace('$', '.');
738: // if (!_thisPackageName.equals("")) {
739: // cName = _thisPackageName + "." + cName;
740: // }
741: // Class c = context.lookupClass(cName);
742: Class<?> c = _loadClassForThis(context);
743: node.setProperty(NodeProperties.TYPE, c);
744: node.setProperty(NodeProperties.MODIFIER,
745: context.getModifier(node));
746: return c;
747: } else
748: return super.visit(node);
749: }
750:
751: };
752: }
753: }
754:
755: }
|