Source Code Cross Referenced for TypeChecker.java in  » Template-Engine » Tea » com » go » tea » compiler » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Template Engine » Tea » com.go.tea.compiler 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* ====================================================================
0002:         * Tea - Copyright (c) 1997-2000 Walt Disney Internet Group
0003:         * ====================================================================
0004:         * The Tea Software License, Version 1.1
0005:         *
0006:         * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
0007:         *
0008:         * Redistribution and use in source and binary forms, with or without
0009:         * modification, are permitted provided that the following conditions
0010:         * are met:
0011:         *
0012:         * 1. Redistributions of source code must retain the above copyright
0013:         *    notice, this list of conditions and the following disclaimer.
0014:         *
0015:         * 2. Redistributions in binary form must reproduce the above copyright
0016:         *    notice, this list of conditions and the following disclaimer in
0017:         *    the documentation and/or other materials provided with the
0018:         *    distribution.
0019:         *
0020:         * 3. The end-user documentation included with the redistribution,
0021:         *    if any, must include the following acknowledgment:
0022:         *       "This product includes software developed by the
0023:         *        Walt Disney Internet Group (http://opensource.go.com/)."
0024:         *    Alternately, this acknowledgment may appear in the software itself,
0025:         *    if and wherever such third-party acknowledgments normally appear.
0026:         *
0027:         * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
0028:         *    not be used to endorse or promote products derived from this
0029:         *    software without prior written permission. For written
0030:         *    permission, please contact opensource@dig.com.
0031:         *
0032:         * 5. Products derived from this software may not be called "Tea",
0033:         *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
0034:         *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
0035:         *    written permission of the Walt Disney Internet Group.
0036:         *
0037:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0038:         * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0039:         * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0040:         * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
0041:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0042:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0043:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
0044:         * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0045:         * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
0046:         * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0047:         * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0048:         * ====================================================================
0049:         *
0050:         * For more information about Tea, please see http://opensource.go.com/.
0051:         */
0052:
0053:        package com.go.tea.compiler;
0054:
0055:        import java.util.Vector;
0056:        import java.util.Collection;
0057:        import java.util.Map;
0058:        import java.util.List;
0059:        import java.util.ArrayList;
0060:        import java.util.Stack;
0061:        import java.lang.reflect.Array;
0062:        import java.lang.reflect.Method;
0063:        import java.lang.reflect.Modifier;
0064:        import java.beans.*;
0065:        import com.go.tea.parsetree.*;
0066:        import com.go.tea.util.BeanAnalyzer;
0067:
0068:        /******************************************************************************
0069:         * A TypeChecker operates on a template's parse tree, created by a
0070:         * {@link Parser}, filling in type information while it checks the validity of
0071:         * the whole template. After a template has been type-checked and there are no
0072:         * errors, the template is ready for code generation. Add an
0073:         * {@link ErrorListener} to capture any semantic errors detected by the
0074:         * TypeChecker.
0075:         *
0076:         * @author Brian S O'Neill
0077:         * @version
0078:         * <!--$$Revision:--> 111 <!-- $-->, <!--$$JustDate:-->  7/26/01 <!-- $-->
0079:         */
0080:        public class TypeChecker {
0081:            private CompilationUnit mUnit;
0082:            private String[] mImports;
0083:
0084:            private Vector mListeners = new Vector(1);
0085:            private int mErrorCount = 0;
0086:
0087:            private ClassLoader mClassLoader;
0088:            private boolean mExceptionGuardian;
0089:
0090:            private MessageFormatter mFormatter;
0091:            private int mForeachCount;
0092:
0093:            public TypeChecker(CompilationUnit unit) {
0094:                mUnit = unit;
0095:                mImports = mUnit.getImportedPackages();
0096:                mFormatter = MessageFormatter.lookup(this );
0097:            }
0098:
0099:            public void addErrorListener(ErrorListener listener) {
0100:                mListeners.addElement(listener);
0101:            }
0102:
0103:            public void removeErrorListener(ErrorListener listener) {
0104:                mListeners.removeElement(listener);
0105:            }
0106:
0107:            private void dispatchParseError(ErrorEvent e) {
0108:                mErrorCount++;
0109:
0110:                synchronized (mListeners) {
0111:                    for (int i = 0; i < mListeners.size(); i++) {
0112:                        ((ErrorListener) mListeners.elementAt(i))
0113:                                .compileError(e);
0114:                    }
0115:                }
0116:            }
0117:
0118:            private void error(String str, Node culprit) {
0119:                str = mFormatter.format(str);
0120:                dispatchParseError(new ErrorEvent(this , str, culprit
0121:                        .getSourceInfo(), mUnit));
0122:            }
0123:
0124:            private void error(String str, String arg, Node culprit) {
0125:                str = mFormatter.format(str, arg);
0126:                dispatchParseError(new ErrorEvent(this , str, culprit
0127:                        .getSourceInfo(), mUnit));
0128:            }
0129:
0130:            private void error(String str, String arg1, String arg2,
0131:                    Node culprit) {
0132:                str = mFormatter.format(str, arg1, arg2);
0133:                dispatchParseError(new ErrorEvent(this , str, culprit
0134:                        .getSourceInfo(), mUnit));
0135:            }
0136:
0137:            private void error(String str, String arg1, String arg2,
0138:                    String arg3, Node culprit) {
0139:                str = mFormatter.format(str, arg1, arg2, arg3);
0140:                dispatchParseError(new ErrorEvent(this , str, culprit
0141:                        .getSourceInfo(), mUnit));
0142:            }
0143:
0144:            private void error(String str, Token culprit) {
0145:                str = mFormatter.format(str);
0146:                dispatchParseError(new ErrorEvent(this , str, culprit
0147:                        .getSourceInfo(), mUnit));
0148:            }
0149:
0150:            private void error(String str, String arg, Token culprit) {
0151:                str = mFormatter.format(str, arg);
0152:                dispatchParseError(new ErrorEvent(this , str, culprit
0153:                        .getSourceInfo(), mUnit));
0154:            }
0155:
0156:            private void error(String str, SourceInfo info) {
0157:                str = mFormatter.format(str);
0158:                dispatchParseError(new ErrorEvent(this , str, info, mUnit));
0159:            }
0160:
0161:            private void error(String str, String arg, SourceInfo info) {
0162:                str = mFormatter.format(str, arg);
0163:                dispatchParseError(new ErrorEvent(this , str, info, mUnit));
0164:            }
0165:
0166:            private void error(String str, String arg1, String arg2,
0167:                    SourceInfo info) {
0168:                str = mFormatter.format(str, arg1, arg2);
0169:                dispatchParseError(new ErrorEvent(this , str, info, mUnit));
0170:            }
0171:
0172:            /**
0173:             * Sets the ClassLoader to use to load classes with. If set to null,
0174:             * then classes are loaded using Class.forName.
0175:             */
0176:            public void setClassLoader(ClassLoader loader) {
0177:                mClassLoader = loader;
0178:            }
0179:
0180:            /**
0181:             * Enabling Exception Guardian causes statements that might throw a
0182:             * exception to be guarded. By default, this feature is off. If a guarded
0183:             * statement throws a RuntimeException, it is caught and handed off to
0184:             * {@link ThreadGroup.uncaughtException}. Execution then proceeds to the
0185:             * next statement. If the guarded statement is an assignment, then the
0186:             * variable is assigned null if an exception is caught.
0187:             */
0188:            public void setExceptionGuardianEnabled(boolean enabled) {
0189:                mExceptionGuardian = enabled;
0190:            }
0191:
0192:            private Class loadClass(String name) throws ClassNotFoundException {
0193:                while (true) {
0194:                    try {
0195:                        if (mClassLoader == null) {
0196:                            return Class.forName(name);
0197:                        } else {
0198:                            return mClassLoader.loadClass(name);
0199:                        }
0200:                    } catch (ClassNotFoundException e) {
0201:                        int index = name.lastIndexOf('.');
0202:                        if (index < 0) {
0203:                            throw e;
0204:                        }
0205:
0206:                        // Search for inner class.
0207:                        name = name.substring(0, index) + '$'
0208:                                + name.substring(index + 1);
0209:                    }
0210:                }
0211:            }
0212:
0213:            public void typeCheck() {
0214:                Template template = mUnit.getParseTree();
0215:                Name name = template.getName();
0216:
0217:                if (name != null) {
0218:                    if (!(mUnit.getShortName().equals(name.getName()))) {
0219:                        error("template.name", mUnit.getName(), name);
0220:                    }
0221:                }
0222:
0223:                // Convert some ExpressionStatements into ReturnStatements.
0224:                template.accept(new ReturnConvertor());
0225:
0226:                // Reduce the amount of concatenation operations.
0227:                // It is important that this step be performed after using the
0228:                // ReturnConvertor and before the main type checking. Otherwise, a
0229:                // return may only capture a partial result or an expression may be
0230:                // set to a type which is only appropriate for concatenation.
0231:                //
0232:                // Note: Decreasing the amount of concatenations can have a slight
0233:                // impact on the language semantics depending on how the context is
0234:                // defined. Generally, reducing the amount of concatenations improves
0235:                // performance. If reducing the amount of concatenations hinders
0236:                // performance, then the data buffering and/or output strategy in the
0237:                // context may need to be improved.
0238:                //
0239:                //template.accept(new ConcatenationReducer());
0240:
0241:                // Increase the amount of concatenation operations.
0242:                // It is important that this step be performed after using the
0243:                // ReturnConvertor and before the main type checking. Otherwise, a
0244:                // return may capture too large of a result or an expression may be
0245:                // set to a type which is only appropriate for output.
0246:                //
0247:                // Note: Increasing the amount of concatenations can have a profound
0248:                // impact on the language semantics depending on how the context is
0249:                // defined. Generally, increasing the amount of concatenations hinders
0250:                // performance. If increasing the amount of concatenations improves
0251:                // performance, then the data buffering and/or output strategy in the
0252:                // context may need to be improved.
0253:                //
0254:                //template.accept(new ConcatenationIncreaser());
0255:
0256:                // Perform major type checking.
0257:                new Visitor().check(template);
0258:
0259:                if (mErrorCount == 0 && mExceptionGuardian) {
0260:                    // Equip the parse tree with ExceptionGuard statements.
0261:                    template.accept(new ExceptionGuardian());
0262:                }
0263:            }
0264:
0265:            public int getErrorCount() {
0266:                return mErrorCount;
0267:            }
0268:
0269:            private class Visitor extends TreeWalker {
0270:                private Type mReturnType;
0271:                private Scope mScope = new Scope();
0272:
0273:                private Stack mLoopVariables;
0274:
0275:                public Visitor() {
0276:                    super ();
0277:                    mLoopVariables = new Stack();
0278:                }
0279:
0280:                private Scope enterScope() {
0281:                    return mScope = new Scope(mScope);
0282:                }
0283:
0284:                private Scope exitScope() {
0285:                    return mScope = mScope.getParent();
0286:                }
0287:
0288:                private void defineVariable(VariableRef ref, Type type) {
0289:                    mScope.declareVariable(new Variable(ref.getSourceInfo(),
0290:                            ref.getName(), type));
0291:
0292:                    if (!mScope.bindToVariable(ref)) {
0293:                        // Shouldn't happen.
0294:                        error("variable.undefined", ref.getName(), ref);
0295:                    }
0296:                }
0297:
0298:                public void check(Node node) {
0299:                    node.accept(this );
0300:                }
0301:
0302:                private void checkAccess(Class clazz, Node node) {
0303:                    if (!(Modifier.isPublic(clazz.getModifiers()))) {
0304:                        error("access.check", clazz.getName(), node);
0305:                    }
0306:                }
0307:
0308:                public Object visit(Template node) {
0309:                    enterScope();
0310:
0311:                    Variable[] declared = node.getParams();
0312:                    if (declared != null) {
0313:                        for (int i = 0; i < declared.length; i++) {
0314:                            check(declared[i]);
0315:                        }
0316:                    }
0317:
0318:                    Statement stmt = node.getStatement();
0319:                    if (stmt != null) {
0320:                        check(stmt);
0321:                    }
0322:
0323:                    Type returnType = mReturnType;
0324:                    if (returnType == null) {
0325:                        returnType = Type.VOID_TYPE;
0326:                    }
0327:                    node.setReturnType(returnType);
0328:
0329:                    exitScope();
0330:                    return null;
0331:                }
0332:
0333:                public Object visit(Name node) {
0334:                    return null;
0335:                }
0336:
0337:                public Object visit(TypeName node) {
0338:                    // Check that type name is a valid java class.
0339:                    String name = node.getName();
0340:                    String checkName = name;
0341:                    String errorMsg = null;
0342:
0343:                    for (int i = -1; i < mImports.length; i++) {
0344:                        if (i >= 0) {
0345:                            checkName = mImports[i] + '.' + name;
0346:                        }
0347:
0348:                        try {
0349:                            Class clazz = loadClass(checkName);
0350:                            checkAccess(clazz, node);
0351:
0352:                            // Found class, check if should be array.
0353:                            int dim = node.getDimensions();
0354:                            if (dim > 0) {
0355:                                clazz = Array.newInstance(clazz, new int[dim])
0356:                                        .getClass();
0357:                            }
0358:
0359:                            node.setType(new Type(clazz));
0360:                            errorMsg = null;
0361:                            break;
0362:                        } catch (ClassNotFoundException e) {
0363:                            if (errorMsg == null) {
0364:                                errorMsg = mFormatter.format(
0365:                                        "typename.unknown", name);
0366:                            }
0367:                        } catch (RuntimeException e) {
0368:                            error(e.toString(), node);
0369:                        } catch (LinkageError e) {
0370:                            error(e.toString(), node);
0371:                            return null;
0372:                        }
0373:                    }
0374:
0375:                    // Fall through to here only if class wasn't found.
0376:                    if (errorMsg != null) {
0377:                        error(errorMsg, node);
0378:                    }
0379:
0380:                    return null;
0381:                }
0382:
0383:                public Object visit(Variable node) {
0384:                    String name = node.getName();
0385:                    Variable v = mScope.getDeclaredVariable(name);
0386:                    if (v == null) {
0387:                        mScope.declareVariable(node);
0388:                    } else {
0389:                        error("variable.declared", name, node);
0390:                        error("variable.declared.here", name, v);
0391:                    }
0392:
0393:                    TypeName typeName = node.getTypeName();
0394:                    check(typeName);
0395:                    node.setType(typeName.getType());
0396:
0397:                    return null;
0398:                }
0399:
0400:                public Object visit(ExpressionList node) {
0401:                    Expression[] exprs = node.getExpressions();
0402:                    for (int i = 0; i < exprs.length; i++) {
0403:                        check(exprs[i]);
0404:                    }
0405:                    return null;
0406:                }
0407:
0408:                public Object visit(Statement node) {
0409:                    return null;
0410:                }
0411:
0412:                public Object visit(StatementList node) {
0413:                    Statement[] stmts = node.getStatements();
0414:                    for (int i = 0; i < stmts.length; i++) {
0415:                        if (i > 0 && stmts[i - 1].isBreak()
0416:                                && stmts[i].getClass() != Statement.class) {
0417:                            error("break.code.unreachable", stmts[i]);
0418:                        }
0419:                        check(stmts[i]);
0420:                    }
0421:                    return null;
0422:                }
0423:
0424:                public Object visit(Block node) {
0425:                    return visit((StatementList) node);
0426:                }
0427:
0428:                public Object visit(AssignmentStatement node) {
0429:                    VariableRef lvalue = node.getLValue();
0430:                    String lname = lvalue.getName();
0431:                    if (mLoopVariables.contains(lname)) {
0432:                        error("foreach.loopvar.nomodify", lname, node
0433:                                .getSourceInfo());
0434:                        return null;
0435:                    }
0436:
0437:                    Expression rvalue = node.getRValue();
0438:                    check(rvalue);
0439:
0440:                    Type type = rvalue.getType();
0441:
0442:                    if (type != null) {
0443:                        if (mExceptionGuardian && rvalue.isExceptionPossible()
0444:                                && type.isNonNull()) {
0445:
0446:                            // Since the expression may throw an exception and be
0447:                            // guarded, an assignment of null needs to work.
0448:
0449:                            rvalue.convertTo(type.toNullable());
0450:                            type = rvalue.getType();
0451:                        }
0452:                        defineVariable(lvalue, type);
0453:                    }
0454:
0455:                    return null;
0456:                }
0457:
0458:                public Object visit(BreakStatement node) {
0459:                    // check to make sure that the break is contained in a loop
0460:                    if (mForeachCount <= 0) {
0461:                        error("break.not.inside.foreach", node);
0462:                    }
0463:
0464:                    return null;
0465:                }
0466:
0467:                public Object visit(ForeachStatement node) {
0468:
0469:                    ++mForeachCount;
0470:
0471:                    VariableRef loopVar = node.getLoopVariable();
0472:                    String varName = loopVar.getName();
0473:                    if (mLoopVariables.contains(varName)) {
0474:                        error("foreach.loopvar.noreuse", varName, loopVar
0475:                                .getSourceInfo());
0476:                        return null;
0477:                    }
0478:                    mLoopVariables.push(varName);
0479:
0480:                    Expression range = node.getRange();
0481:                    Expression endRange = node.getEndRange();
0482:                    Block body = node.getBody();
0483:
0484:                    check(range);
0485:                    Type rangeType = range.getType();
0486:
0487:                    Type endRangeType;
0488:                    if (endRange == null) {
0489:                        endRangeType = null;
0490:                    } else {
0491:                        check(endRange);
0492:                        endRangeType = endRange.getType();
0493:                    }
0494:
0495:                    if (rangeType == null
0496:                            || (endRange != null && endRangeType == null)) {
0497:
0498:                        // Some error must have been detected in the range, so just
0499:                        // check everything else possible and bail out.
0500:
0501:                        enterScope();
0502:                        check(body);
0503:                        exitScope();
0504:                        return null;
0505:                    }
0506:
0507:                    Class rangeClass = rangeType.getObjectClass();
0508:                    Type elementType;
0509:
0510:                    // If there is no end range, then range expression type must be 
0511:                    // an array or Collection.
0512:
0513:                    if (endRangeType == null) {
0514:                        try {
0515:                            elementType = rangeType.getIterationElementType();
0516:                        } catch (IntrospectionException e) {
0517:                            error(e.toString(), node);
0518:                            return null;
0519:                        }
0520:
0521:                        if (elementType == null) {
0522:                            error("foreach.iteration.not.supported", rangeType
0523:                                    .getSimpleName(), range);
0524:                        } else {
0525:                            checkAccess(elementType.getNaturalClass(), range);
0526:
0527:                            if (node.isReverse()
0528:                                    && !rangeType.isReverseIterationSupported()) {
0529:                                error("foreach.reverse.not.supported",
0530:                                        rangeType.getSimpleName(), range);
0531:                            }
0532:                        }
0533:                    } else {
0534:                        elementType = Type.INT_TYPE;
0535:
0536:                        if (!Number.class.isAssignableFrom(rangeClass)) {
0537:                            error("foreach.range.start", rangeType
0538:                                    .getSimpleName(), range);
0539:                            range.setType(elementType);
0540:                        } else {
0541:                            if (Long.class.isAssignableFrom(rangeClass)) {
0542:                                elementType = Type.LONG_TYPE;
0543:                            }
0544:                        }
0545:
0546:                        Class endRangeClass = endRangeType.getObjectClass();
0547:                        if (!Number.class.isAssignableFrom(endRangeClass)) {
0548:                            error("foreach.range.end", endRangeType
0549:                                    .getSimpleName(), endRange);
0550:                            endRange.setType(elementType);
0551:                        } else {
0552:                            if (Long.class.isAssignableFrom(endRangeClass)) {
0553:                                elementType = Type.LONG_TYPE;
0554:                            }
0555:                        }
0556:
0557:                        range.convertTo(elementType);
0558:                        endRange.convertTo(elementType);
0559:                    }
0560:
0561:                    // Enter a new scope because variable declarations local
0562:                    // to a foreach loop might never get a value assigned because
0563:                    // there is no guarantee that the body will ever execute.
0564:                    Scope bodyScope = enterScope();
0565:
0566:                    if (elementType != null) {
0567:                        defineVariable(loopVar, elementType);
0568:                    }
0569:
0570:                    check(body);
0571:                    exitScope();
0572:
0573:                    Variable[] vars = bodyScope.promote();
0574:
0575:                    if (vars.length > 0) {
0576:                        // Since the body can be executed multiple times, variables
0577:                        // must be of the correct type before entering the scope.
0578:                        node.setInitializer(createConversions(mScope, vars,
0579:                                node.getSourceInfo()));
0580:
0581:                        // Apply any conversions at the end of the body as well in
0582:                        // order for the variables to be of the correct type when
0583:                        // accessed again at the start of the body.
0584:                        body.setFinalizer(createConversions(bodyScope, vars,
0585:                                body.getSourceInfo()));
0586:
0587:                        // Declare all promoted variables outside body scope.
0588:                        mScope.declareVariables(vars);
0589:
0590:                        // Re-check body to account for variable declaration changes.
0591:                        if (mErrorCount == 0) {
0592:                            bodyScope.delete();
0593:                            bodyScope = enterScope();
0594:
0595:                            if (elementType != null) {
0596:                                defineVariable(loopVar, elementType);
0597:                            }
0598:
0599:                            check(body);
0600:                            exitScope();
0601:                        }
0602:                    }
0603:
0604:                    mLoopVariables.pop();
0605:                    --mForeachCount;
0606:                    return null;
0607:                }
0608:
0609:                public Object visit(IfStatement node) {
0610:                    Expression condition = node.getCondition();
0611:                    Block thenPart = node.getThenPart();
0612:                    Block elsePart = node.getElsePart();
0613:                    Variable[] thenCasts = null;
0614:                    Variable[] elseCasts = null;
0615:
0616:                    int preCondErrorCount = mErrorCount;
0617:                    check(condition);
0618:
0619:                    Type type = condition.getType();
0620:                    if (type != null && type.getObjectClass() != Boolean.class) {
0621:                        error("if.condition", condition);
0622:                    } else {
0623:                        condition.convertTo(Type.BOOLEAN_TYPE);
0624:                        if (preCondErrorCount == mErrorCount) {
0625:                            IsaDetector detector = new IsaDetector();
0626:                            condition.accept(detector);
0627:                            thenCasts = detector.getThenCasts();
0628:                            elseCasts = detector.getElseCasts();
0629:                        }
0630:                    }
0631:
0632:                    Scope thenScope = null;
0633:                    Scope elseScope = null;
0634:
0635:                    thenScope = enterScope();
0636:                    if (thenPart != null) {
0637:                        Statement stmt = createCasts(thenScope, thenCasts,
0638:                                thenPart.getSourceInfo());
0639:                        // Don't typecheck any inserted casts, they are already correct
0640:                        check(thenPart);
0641:                        thenPart.setInitializer(stmt);
0642:                    }
0643:                    exitScope();
0644:
0645:                    elseScope = enterScope();
0646:                    if (elsePart != null) {
0647:                        Statement stmt = createCasts(elseScope, elseCasts,
0648:                                elsePart.getSourceInfo());
0649:                        // Don't typecheck any inserted casts, they are already correct
0650:                        check(elsePart);
0651:                        elsePart.setInitializer(stmt);
0652:                    }
0653:                    exitScope();
0654:
0655:                    // Merge then and else scopes
0656:                    Variable[] vars = thenScope.intersect(elseScope);
0657:
0658:                    if (vars.length == 0) {
0659:                        node.setMergedVariables(vars);
0660:                    } else {
0661:                        if (mExceptionGuardian
0662:                                && condition.isExceptionPossible()) {
0663:                            // If condition could throw an exception, then the
0664:                            // exception handler needs to ensure that these variables
0665:                            // are assigned null. Therefore, the variables need to be
0666:                            // able to accept null.
0667:                            for (int i = 0; i < vars.length; i++) {
0668:                                Variable v = vars[i];
0669:                                Type t = v.getType();
0670:                                if (t.isNonNull()) {
0671:                                    v = (Variable) v.clone();
0672:                                    v.setType(t.toNullable());
0673:                                    vars[i] = v;
0674:                                }
0675:                            }
0676:                        }
0677:
0678:                        node.setMergedVariables(vars);
0679:
0680:                        // Add corrective code as a result of merging.
0681:                        if (thenPart == null) {
0682:                            thenPart = new Block(node.getSourceInfo());
0683:                            node.setThenPart(thenPart);
0684:                        }
0685:                        Statement fin = createConversions(thenScope, vars,
0686:                                thenPart.getSourceInfo());
0687:                        thenPart.setFinalizer(fin);
0688:
0689:                        if (elsePart == null) {
0690:                            elsePart = new Block(node.getSourceInfo());
0691:                            node.setElsePart(elsePart);
0692:                        }
0693:                        fin = createConversions(elseScope, vars, elsePart
0694:                                .getSourceInfo());
0695:                        elsePart.setFinalizer(fin);
0696:
0697:                        // Re-declare all new variables with new types for future
0698:                        // variable references.
0699:                        mScope.declareVariables(vars);
0700:                    }
0701:
0702:                    return null;
0703:                }
0704:
0705:                /**
0706:                 * Create variable casting assignment statements.
0707:                 *
0708:                 * @param scope scope of statement
0709:                 * @param newVars optional new variables to assign to
0710:                 * @param info source info to apply to created assignments
0711:                 * @return statement with assignments or null if none
0712:                 */
0713:                private Statement createCasts(Scope scope, Variable[] newVars,
0714:                        SourceInfo info) {
0715:                    if (newVars == null) {
0716:                        return null;
0717:                    }
0718:
0719:                    int length = newVars.length;
0720:                    if (length == 0) {
0721:                        return null;
0722:                    }
0723:
0724:                    List newStatements = new ArrayList(length);
0725:
0726:                    for (int i = 0; i < length; i++) {
0727:                        Variable newVar = newVars[i];
0728:                        String name = newVar.getName();
0729:                        Variable oldVar = scope.getDeclaredVariable(name);
0730:
0731:                        // It is important that the variable be declared private.
0732:                        newVar = scope.declareVariable(newVar, true);
0733:
0734:                        if (newVar == oldVar) {
0735:                            continue;
0736:                        }
0737:
0738:                        VariableRef lvalue = new VariableRef(info, name);
0739:                        scope.bindToVariable(lvalue);
0740:                        lvalue.setVariable(newVar);
0741:
0742:                        VariableRef rvalue = new VariableRef(info, name);
0743:                        scope.bindToVariable(rvalue);
0744:                        rvalue.setVariable(oldVar);
0745:                        try {
0746:                            rvalue.convertTo(newVar.getType(), true);
0747:                        } catch (IllegalArgumentException e) {
0748:                            if (mErrorCount == 0) {
0749:                                throw e;
0750:                            }
0751:                        }
0752:
0753:                        newStatements.add(new AssignmentStatement(info, lvalue,
0754:                                rvalue));
0755:                    }
0756:
0757:                    if (newStatements.size() == 0) {
0758:                        return null;
0759:                    } else {
0760:                        Statement[] stmts = new Statement[newStatements.size()];
0761:                        stmts = (Statement[]) newStatements.toArray(stmts);
0762:                        return new StatementList(info, stmts);
0763:                    }
0764:                }
0765:
0766:                /**
0767:                 * Create variable conversion assignment statements.
0768:                 *
0769:                 * @param scope scope of statement
0770:                 * @param newVars optional new variables to assign to
0771:                 * @param info source info to apply to created assignments
0772:                 * @return statement with assignments or null if none
0773:                 */
0774:                private Statement createConversions(Scope scope,
0775:                        Variable[] newVars, SourceInfo info) {
0776:                    if (newVars == null) {
0777:                        return null;
0778:                    }
0779:
0780:                    int length = newVars.length;
0781:                    if (length == 0) {
0782:                        return null;
0783:                    }
0784:
0785:                    List newStatements = new ArrayList(length);
0786:
0787:                    for (int i = 0; i < length; i++) {
0788:                        Variable newVar = newVars[i];
0789:                        String name = newVar.getName();
0790:                        Variable oldVar = scope.getDeclaredVariable(name);
0791:
0792:                        newVar = scope.declareVariable(newVar);
0793:
0794:                        if (newVar == oldVar) {
0795:                            continue;
0796:                        }
0797:
0798:                        VariableRef lvalue = new VariableRef(info, name);
0799:                        scope.bindToVariable(lvalue);
0800:                        lvalue.setVariable(newVar);
0801:
0802:                        VariableRef rvalue = new VariableRef(info, name);
0803:                        scope.bindToVariable(rvalue);
0804:                        rvalue.setVariable(oldVar);
0805:                        rvalue.convertTo(newVar.getType(), false);
0806:
0807:                        newStatements.add(new AssignmentStatement(info, lvalue,
0808:                                rvalue));
0809:                    }
0810:
0811:                    if (newStatements.size() == 0) {
0812:                        return null;
0813:                    } else {
0814:                        Statement[] stmts = new Statement[newStatements.size()];
0815:                        stmts = (Statement[]) newStatements.toArray(stmts);
0816:                        return new StatementList(info, stmts);
0817:                    }
0818:                }
0819:
0820:                public Object visit(SubstitutionStatement node) {
0821:                    // Check if substitution allowed in this template.
0822:                    if (!mUnit.getParseTree().hasSubstitutionParam()) {
0823:                        error("substitution.undeclared", node);
0824:                    }
0825:
0826:                    return null;
0827:                }
0828:
0829:                public Object visit(ExpressionStatement node) {
0830:                    Expression expr = node.getExpression();
0831:                    if (expr instanceof  CallExpression) {
0832:                        ((CallExpression) expr).setVoidPermitted(true);
0833:                    }
0834:                    check(expr);
0835:
0836:                    Type type = expr.getType();
0837:                    Compiler c = mUnit.getCompiler();
0838:
0839:                    if (type != null && type.getNaturalClass() != void.class
0840:                            && c != null) {
0841:
0842:                        Method[] methods = c.getRuntimeContextMethods();
0843:                        String name = c.getRuntimeReceiver();
0844:
0845:                        int cnt = MethodMatcher.match(methods, name,
0846:                                new Type[] { type });
0847:
0848:                        if (cnt < 1) {
0849:                            error("expressionstatement.receiver", expr
0850:                                    .getType().getSimpleName(), node);
0851:                        } else {
0852:                            Method receiver = methods[0];
0853:                            node.setReceiverMethod(receiver);
0854:                            expr.convertTo(new Type(receiver
0855:                                    .getParameterTypes()[0]));
0856:                        }
0857:                    }
0858:
0859:                    return null;
0860:                }
0861:
0862:                public Object visit(ReturnStatement node) {
0863:                    Type type;
0864:                    Expression expr = node.getExpression();
0865:
0866:                    if (expr != null) {
0867:                        check(expr);
0868:                        type = expr.getType();
0869:                    } else {
0870:                        type = Type.VOID_TYPE;
0871:                    }
0872:
0873:                    if (mReturnType == null) {
0874:                        mReturnType = type;
0875:                    } else {
0876:                        Type newType = mReturnType.getCompatibleType(type);
0877:                        if (newType == null) {
0878:                            error("returnstatement.type", type.getSimpleName(),
0879:                                    mReturnType.getSimpleName(), node);
0880:                        }
0881:                        mReturnType = newType;
0882:                    }
0883:
0884:                    return null;
0885:                }
0886:
0887:                public Object visit(Expression node) {
0888:                    return null;
0889:                }
0890:
0891:                public Object visit(ParenExpression node) {
0892:                    Expression expr = node.getExpression();
0893:                    check(expr);
0894:                    return null;
0895:                }
0896:
0897:                public Object visit(NewArrayExpression node) {
0898:                    ExpressionList list = node.getExpressionList();
0899:                    check(list);
0900:
0901:                    Expression[] exprs = list.getExpressions();
0902:
0903:                    if (node.isAssociative()) {
0904:                        if (exprs.length % 2 != 0) {
0905:                            error("newarrayexpression.associative", node);
0906:                        }
0907:
0908:                        Type elementType = newArrayElementType(exprs, 1, 2);
0909:                        // Since an element might not be found for a given key, the
0910:                        // element type is set to be nullable.
0911:                        elementType = elementType.toNonPrimitive().toNullable();
0912:
0913:                        Type arrayType = new Type(Map.class);
0914:                        try {
0915:                            arrayType = arrayType
0916:                                    .setArrayElementType(elementType);
0917:                        } catch (IntrospectionException e) {
0918:                            error(e.toString(), node);
0919:                            return null;
0920:                        }
0921:                        node.setType(arrayType);
0922:
0923:                        // Make sure all keys are converted to Objects.
0924:                        for (int i = 0; i < exprs.length; i += 2) {
0925:                            exprs[i].convertTo(Type.OBJECT_TYPE);
0926:                        }
0927:                    } else {
0928:                        Type elementType = newArrayElementType(exprs, 0, 1);
0929:                        Class elementClass = elementType.getNaturalClass();
0930:                        Class arrayClass = Array.newInstance(elementClass, 0)
0931:                                .getClass();
0932:
0933:                        Type arrayType = new Type(arrayClass);
0934:                        if (elementType.isNonNull()) {
0935:                            try {
0936:                                arrayType = arrayType
0937:                                        .setArrayElementType(elementType);
0938:                            } catch (IntrospectionException e) {
0939:                            }
0940:                        }
0941:                        node.setType(arrayType);
0942:                    }
0943:
0944:                    return null;
0945:                }
0946:
0947:                private Type newArrayElementType(Expression[] exprs, int start,
0948:                        int increment) {
0949:                    Type elementType = null;
0950:
0951:                    for (int i = start; i < exprs.length; i += increment) {
0952:                        Type type = exprs[i].getType();
0953:
0954:                        if (elementType == null) {
0955:                            elementType = type;
0956:                        } else {
0957:                            elementType = elementType.getCompatibleType(type);
0958:                        }
0959:                    }
0960:
0961:                    if (elementType == null) {
0962:                        elementType = Type.NULL_TYPE;
0963:                    }
0964:
0965:                    return elementType;
0966:                }
0967:
0968:                public Object visit(FunctionCallExpression node) {
0969:                    if (!initialCallExpressionCheck(node)) {
0970:                        return null;
0971:                    }
0972:
0973:                    Expression[] exprs = node.getParams().getExpressions();
0974:                    int length = exprs.length;
0975:                    Type[] actualTypes = new Type[length];
0976:                    for (int i = 0; i < length; i++) {
0977:                        actualTypes[i] = exprs[i].getType();
0978:                    }
0979:
0980:                    Block subParam = node.getSubstitutionParam();
0981:                    Compiler compiler = mUnit.getCompiler();
0982:                    String name = node.getTarget().getName();
0983:
0984:                    // Look for Java function to call.
0985:                    name = name.replace('.', '$');
0986:
0987:                    if (subParam != null) {
0988:                        Type[] types = new Type[length + 1];
0989:                        System.arraycopy(actualTypes, 0, types, 0, length);
0990:                        types[length] = new Type(
0991:                                com.go.tea.runtime.Substitution.class);
0992:                        actualTypes = types;
0993:                    }
0994:
0995:                    Method[] methods = compiler.getRuntimeContextMethods();
0996:                    int cnt = MethodMatcher.match(methods, name, actualTypes);
0997:                    if (cnt <= 0) {
0998:                        error("functioncallexpression.not.found", node);
0999:                        return null;
1000:                    }
1001:
1002:                    Method m = methods[0];
1003:                    node.setCalledMethod(m);
1004:
1005:                    if (m.getDeclaringClass() == Object.class) {
1006:                        error("functioncallexpression.not.found", node);
1007:                    }
1008:
1009:                    // Set param types.
1010:                    Class[] paramClasses = m.getParameterTypes();
1011:                    for (int i = 0; i < length; i++) {
1012:                        exprs[i].convertTo(new Type(paramClasses[i]), false);
1013:                    }
1014:
1015:                    Class retClass = m.getReturnType();
1016:                    checkAccess(retClass, node);
1017:                    Type retType;
1018:                    if (retClass == char.class) {
1019:                        // Convert any returned char to a String.
1020:                        retType = Type.NON_NULL_STRING_TYPE;
1021:                    } else if (retClass == Character.class) {
1022:                        // Convert any returned Character to a String.
1023:                        retType = Type.STRING_TYPE;
1024:                    } else {
1025:                        retType = new Type(retClass);
1026:                    }
1027:                    node.setType(retType);
1028:
1029:                    if (retClass == void.class && !node.isVoidPermitted()) {
1030:                        error("functioncallexpression.function.void", node);
1031:                    }
1032:
1033:                    return null;
1034:                }
1035:
1036:                public Object visit(TemplateCallExpression node) {
1037:                    if (!initialCallExpressionCheck(node)) {
1038:                        return null;
1039:                    }
1040:
1041:                    Expression[] exprs = node.getParams().getExpressions();
1042:                    int length = exprs.length;
1043:                    Type[] actualTypes = new Type[length];
1044:                    for (int i = 0; i < length; i++) {
1045:                        actualTypes[i] = exprs[i].getType();
1046:                    }
1047:
1048:                    Block subParam = node.getSubstitutionParam();
1049:                    Compiler compiler = mUnit.getCompiler();
1050:                    String name = node.getTarget().getName();
1051:
1052:                    // Look for a matching template to call.
1053:
1054:                    CompilationUnit unit = compiler.getCompilationUnit(name,
1055:                            mUnit);
1056:                    if (unit == null) {
1057:                        error("templatecallexpression.not.found", node);
1058:                        return null;
1059:                    }
1060:
1061:                    Template tree = unit.getParseTree();
1062:
1063:                    Variable[] formalParams = tree.getParams();
1064:                    if (formalParams != null) {
1065:                        if (formalParams.length != length) {
1066:                            error("templatecallexpression.parameter.count",
1067:                                    String.valueOf(formalParams.length), String
1068:                                            .valueOf(length), node);
1069:                            return null;
1070:                        }
1071:
1072:                        if (subParam != null && !tree.hasSubstitutionParam()) {
1073:                            error("templatecallexpression.substitution.no",
1074:                                    tree.getName().getName(), subParam);
1075:                            return null;
1076:                        } else if (subParam == null
1077:                                && tree.hasSubstitutionParam()) {
1078:                            error("templatecallexpression.substitution.yes",
1079:                                    tree.getName().getName(), node);
1080:                        }
1081:
1082:                        node.setCalledTemplate(unit);
1083:                        for (int i = 0; i < length; i++) {
1084:                            Type type = formalParams[i].getType();
1085:
1086:                            if (type == null) {
1087:                                error(
1088:                                        "templatecallexpression.parameter.unknown",
1089:                                        node);
1090:                                return null;
1091:                            }
1092:
1093:                            int cost = type.convertableFrom(actualTypes[i]);
1094:                            if (cost < 0) {
1095:                                node.setCalledTemplate(null);
1096:
1097:                                String msg1 = actualTypes[i].getFullName();
1098:                                String msg2 = type.getFullName();
1099:
1100:                                ClassLoader CL1 = actualTypes[i]
1101:                                        .getNaturalClass().getClassLoader();
1102:
1103:                                ClassLoader CL2 = type.getNaturalClass()
1104:                                        .getClassLoader();
1105:
1106:                                if (CL1 != CL2) {
1107:                                    msg1 += "(" + CL1 + ")";
1108:                                    msg2 += "(" + CL2 + ")";
1109:                                }
1110:
1111:                                error("templatecallexpression.conversion",
1112:                                        msg1, msg2, exprs[i]);
1113:                            } else {
1114:                                exprs[i].convertTo(type, false);
1115:                            }
1116:                        }
1117:                    }
1118:
1119:                    Type retType = tree.getReturnType();
1120:                    if (retType == null) {
1121:                        retType = Type.VOID_TYPE;
1122:                    }
1123:                    node.setType(retType);
1124:
1125:                    if (Type.VOID_TYPE.equals(retType)
1126:                            && !node.isVoidPermitted()) {
1127:
1128:                        error("templatecallexpression.template.void", node);
1129:                        return null;
1130:                    }
1131:
1132:                    return null;
1133:                }
1134:
1135:                /**
1136:                 * Returns false if initial checks failed.
1137:                 */
1138:                private boolean initialCallExpressionCheck(CallExpression node) {
1139:                    ExpressionList params = node.getParams();
1140:                    check(params);
1141:
1142:                    Statement init = node.getInitializer();
1143:                    if (init != null) {
1144:                        check(init);
1145:                    }
1146:
1147:                    Block subParam = node.getSubstitutionParam();
1148:                    if (subParam != null) {
1149:                        // Enter a new scope because variable declarations local
1150:                        // to a substitution block might never get a value assigned 
1151:                        // because there is no guarantee it will ever execute.
1152:                        Scope subScope = enterScope();
1153:                        check(subParam);
1154:                        exitScope();
1155:
1156:                        Variable[] vars = subScope.promote();
1157:
1158:                        if (vars.length > 0) {
1159:                            // Since the subParam can be executed multiple times,
1160:                            // variables must be of the correct type before entering
1161:                            // the scope.
1162:                            node.setInitializer(createConversions(mScope, vars,
1163:                                    node.getSourceInfo()));
1164:
1165:                            // Apply any conversions at the end of the subParam as well
1166:                            // in order for the variables to be of the correct type
1167:                            // when accessed again at the start of the body.
1168:                            subParam.setFinalizer(createConversions(subScope,
1169:                                    vars, subParam.getSourceInfo()));
1170:
1171:                            // Promoted variables need to become fields
1172:                            for (int i = 0; i < vars.length; i++) {
1173:                                vars[i].setField(true);
1174:                            }
1175:
1176:                            // Declare all promoted variables outside subParam scope.
1177:                            mScope.declareVariables(vars);
1178:
1179:                            // Re-check subParam to account for variable declaration
1180:                            // changes.
1181:                            if (mErrorCount == 0) {
1182:                                subScope.delete();
1183:                                subScope = enterScope();
1184:                                check(subParam);
1185:                                exitScope();
1186:                            }
1187:                        }
1188:
1189:                        // References inside a substitution block to variables
1190:                        // outside need to be fields so that they can be shared.
1191:                        VariableRef[] refs = subScope
1192:                                .getOutOfScopeVariableRefs();
1193:                        for (int i = 0; i < refs.length; i++) {
1194:                            refs[i].getVariable().setField(true);
1195:                        }
1196:                    }
1197:
1198:                    Expression[] exprs = params.getExpressions();
1199:                    int length = exprs.length;
1200:                    for (int i = 0; i < length; i++) {
1201:                        if (exprs[i].getType() == null) {
1202:                            // Type of a parameter is unknown, so bail out.
1203:                            return false;
1204:                        }
1205:                    }
1206:
1207:                    Compiler compiler = mUnit.getCompiler();
1208:                    if (compiler == null) {
1209:                        return false;
1210:                    }
1211:
1212:                    return true;
1213:                }
1214:
1215:                public Object visit(VariableRef node) {
1216:                    if (!mScope.bindToVariable(node)) {
1217:                        error("variableref.undefined", node.getName(), node);
1218:                    }
1219:                    return null;
1220:                }
1221:
1222:                public Object visit(Lookup node) {
1223:                    Expression expr = node.getExpression();
1224:                    check(expr);
1225:
1226:                    // Now check if expression type class contains the lookup name.
1227:                    Type type = expr.getType();
1228:                    String lookupName = node.getLookupName().getName();
1229:
1230:                    if (type != null && lookupName != null) {
1231:                        Class clazz = type.getObjectClass();
1232:                        // Lookup can only work on objects.
1233:                        type = type.toNonPrimitive();
1234:                        expr.convertTo(type);
1235:
1236:                        if ("length".equals(lookupName)) {
1237:                            if (clazz == String.class) {
1238:                                node.setType(Type.INT_TYPE);
1239:                                try {
1240:                                    node.setReadMethod(String.class.getMethod(
1241:                                            "length", new Class[0]));
1242:                                } catch (NoSuchMethodException e) {
1243:                                    throw new LinkageError(e.toString());
1244:                                }
1245:                                return null;
1246:                            } else if (Collection.class.isAssignableFrom(clazz)) {
1247:                                node.setType(Type.INT_TYPE);
1248:                                try {
1249:                                    node.setReadMethod(Collection.class
1250:                                            .getMethod("size", new Class[0]));
1251:                                } catch (NoSuchMethodException e) {
1252:                                    throw new LinkageError(e.toString());
1253:                                }
1254:                                return null;
1255:                            } else if (clazz.isArray()) {
1256:                                node.setType(Type.INT_TYPE);
1257:                                return null;
1258:                            }
1259:                        }
1260:
1261:                        Map properties;
1262:                        try {
1263:                            properties = BeanAnalyzer.getAllProperties(clazz);
1264:                        } catch (IntrospectionException e) {
1265:                            error(e.toString(), node);
1266:                            return null;
1267:                        }
1268:
1269:                        PropertyDescriptor prop = (PropertyDescriptor) properties
1270:                                .get(lookupName);
1271:
1272:                        if (prop == null) {
1273:                            error("lookup.undefined", lookupName, type
1274:                                    .getSimpleName(), node.getLookupName());
1275:                            return null;
1276:                        }
1277:
1278:                        Class retClass = prop.getPropertyType();
1279:                        if (retClass == null) {
1280:                            error("lookup.array.only", lookupName, node
1281:                                    .getLookupName());
1282:                            return null;
1283:                        }
1284:
1285:                        checkAccess(retClass, node.getLookupName());
1286:
1287:                        node.setType(new Type(retClass));
1288:
1289:                        if (retClass == char.class) {
1290:                            // Convert any returned char to a String.
1291:                            node.convertTo(Type.NON_NULL_STRING_TYPE);
1292:                        } else if (retClass == Character.class) {
1293:                            // Convert any returned Character to a String.
1294:                            node.convertTo(Type.STRING_TYPE);
1295:                        }
1296:
1297:                        Method m = prop.getReadMethod();
1298:                        if (m == null) {
1299:                            error("lookup.unreadable", lookupName, node
1300:                                    .getLookupName());
1301:                            return null;
1302:                        }
1303:
1304:                        node.setReadMethod(m);
1305:                    }
1306:
1307:                    return null;
1308:                }
1309:
1310:                public Object visit(ArrayLookup node) {
1311:                    Expression expr = node.getExpression();
1312:                    Expression lookupIndex = node.getLookupIndex();
1313:                    check(expr);
1314:                    check(lookupIndex);
1315:
1316:                    Type type = expr.getType();
1317:                    Type lookupType = lookupIndex.getType();
1318:
1319:                    if (type != null && lookupType != null) {
1320:                        // Array lookup can only work on objects.
1321:                        type = type.toNonPrimitive();
1322:                        expr.convertTo(type);
1323:
1324:                        // Now check if expression type class supports array lookup.
1325:                        // If so, check that lookup index is correct type
1326:
1327:                        Type elementType;
1328:                        try {
1329:                            elementType = type.getArrayElementType();
1330:                        } catch (IntrospectionException e) {
1331:                            error(e.toString(), node);
1332:                            return null;
1333:                        }
1334:
1335:                        if (elementType == null) {
1336:                            error("arraylookup.unsupported", type
1337:                                    .getSimpleName(), node.getLookupToken());
1338:
1339:                            return null;
1340:                        }
1341:
1342:                        checkAccess(elementType.getObjectClass(), node);
1343:
1344:                        // Look for the best array access method.
1345:
1346:                        Method[] methods;
1347:                        try {
1348:                            methods = type.getArrayAccessMethods();
1349:                        } catch (IntrospectionException e) {
1350:                            error(e.toString(), node);
1351:                            return null;
1352:                        }
1353:
1354:                        boolean good = false;
1355:
1356:                        if (methods.length == 0) {
1357:                            // Must be an actual Java array.
1358:                            if (type.getObjectClass().isArray()) {
1359:                                Class lookupClass = lookupType.getObjectClass();
1360:                                if (Number.class.isAssignableFrom(lookupClass)) {
1361:                                    lookupIndex.convertTo(Type.INT_TYPE);
1362:                                    node.setType(elementType);
1363:                                    good = true;
1364:                                }
1365:                            }
1366:                        } else {
1367:                            int count = MethodMatcher.match(methods, null,
1368:                                    new Type[] { lookupType });
1369:                            if (count >= 1) {
1370:                                Method m = methods[0];
1371:                                lookupType = new Type(m.getParameterTypes()[0]);
1372:                                lookupIndex.convertTo(lookupType);
1373:                                node.setReadMethod(m);
1374:                                node.setType(new Type(m.getReturnType()));
1375:                                node.convertTo(elementType);
1376:                                good = true;
1377:                            }
1378:                        }
1379:
1380:                        if (good) {
1381:                            if (elementType.getObjectClass() == Character.class) {
1382:                                // Convert any returned character to a String.
1383:                                node.convertTo(Type.STRING_TYPE);
1384:                            }
1385:                        } else {
1386:                            error("arraylookup.unsupported.for", type
1387:                                    .getSimpleName(), lookupType
1388:                                    .getSimpleName(), lookupIndex);
1389:                        }
1390:                    }
1391:
1392:                    return null;
1393:                }
1394:
1395:                public Object visit(NegateExpression node) {
1396:                    Expression expr = node.getExpression();
1397:                    check(expr);
1398:
1399:                    Type type = expr.getType();
1400:                    if (type == null) {
1401:                        node.setType(Type.INT_TYPE);
1402:                    } else if (Number.class.isAssignableFrom(type
1403:                            .getObjectClass())) {
1404:                        type = type.toPrimitive();
1405:                        expr.convertTo(type);
1406:                        node.setType(type);
1407:                    } else {
1408:                        error("negateexpression.type", node);
1409:                    }
1410:
1411:                    return null;
1412:                }
1413:
1414:                public Object visit(NotExpression node) {
1415:                    Expression expr = node.getExpression();
1416:                    check(expr);
1417:
1418:                    Type type = expr.getType();
1419:                    if (type == null) {
1420:                        node.setType(Type.BOOLEAN_TYPE);
1421:                    } else if (type.getObjectClass() == Boolean.class) {
1422:                        type = type.toPrimitive();
1423:                        expr.convertTo(type);
1424:                        node.setType(type);
1425:                    } else {
1426:                        error("notexpression.type", node);
1427:                    }
1428:
1429:                    return null;
1430:                }
1431:
1432:                public Object visit(ConcatenateExpression node) {
1433:                    Expression left = node.getLeftExpression();
1434:                    Expression right = node.getRightExpression();
1435:
1436:                    check(left);
1437:                    check(right);
1438:
1439:                    left.convertTo(Type.NON_NULL_STRING_TYPE, false);
1440:                    right.convertTo(Type.NON_NULL_STRING_TYPE, false);
1441:                    node.setType(Type.NON_NULL_STRING_TYPE);
1442:
1443:                    return null;
1444:                }
1445:
1446:                public Object visit(ArithmeticExpression node) {
1447:                    Expression left = node.getLeftExpression();
1448:                    Expression right = node.getRightExpression();
1449:
1450:                    check(left);
1451:                    check(right);
1452:
1453:                    Type leftType = left.getType();
1454:                    Type rightType = right.getType();
1455:
1456:                    if (binaryTypeCheck(node, Number.class)) {
1457:                        Type type = leftType.getCompatibleType(rightType)
1458:                                .toPrimitive();
1459:
1460:                        left.convertTo(type);
1461:                        right.convertTo(type);
1462:                        node.setType(type);
1463:                    }
1464:
1465:                    return null;
1466:                }
1467:
1468:                private boolean binaryTypeCheck(BinaryExpression expr,
1469:                        Class clazz) {
1470:                    Expression left = expr.getLeftExpression();
1471:                    Expression right = expr.getRightExpression();
1472:
1473:                    Type leftType = left.getType();
1474:                    Type rightType = right.getType();
1475:
1476:                    if (leftType == null || rightType == null) {
1477:                        return false;
1478:                    }
1479:
1480:                    if (!clazz.isAssignableFrom(leftType.getObjectClass())) {
1481:                        if (!clazz.isAssignableFrom(rightType.getObjectClass())) {
1482:                            String name = new Type(clazz).getSimpleName();
1483:                            error("binaryexpression.type.both", expr
1484:                                    .getOperator().getImage(), name, expr);
1485:                        } else {
1486:                            String name = new Type(clazz).getSimpleName();
1487:                            error("binaryexpression.type.left", expr
1488:                                    .getOperator().getImage(), name, left);
1489:                        }
1490:                    } else if (!clazz.isAssignableFrom(rightType
1491:                            .getObjectClass())) {
1492:                        String name = new Type(clazz).getSimpleName();
1493:                        error("binaryexpression.type.right", expr.getOperator()
1494:                                .getImage(), name, right);
1495:                    } else {
1496:                        return true;
1497:                    }
1498:
1499:                    return false;
1500:                }
1501:
1502:                public Object visit(RelationalExpression node) {
1503:                    node.setType(Type.BOOLEAN_TYPE);
1504:
1505:                    Token token = node.getOperator();
1506:                    int ID = token.getID();
1507:
1508:                    if (ID == Token.ISA) {
1509:                        return visitIsa(node);
1510:                    }
1511:
1512:                    Expression left = node.getLeftExpression();
1513:                    Expression right = node.getRightExpression();
1514:
1515:                    check(left);
1516:                    check(right);
1517:
1518:                    Type leftType = left.getType();
1519:                    Type rightType = right.getType();
1520:
1521:                    if (leftType != null && rightType != null) {
1522:                        Class leftClass = leftType.getNaturalClass();
1523:                        Class rightClass = rightType.getNaturalClass();
1524:
1525:                        Type type;
1526:                        if (ID == Token.EQ || ID == Token.NE) {
1527:                            if (String.class.isAssignableFrom(leftClass)) {
1528:                                type = leftType.toNullable();
1529:                            } else if (String.class
1530:                                    .isAssignableFrom(rightClass)) {
1531:                                type = rightType.toNullable();
1532:                            } else {
1533:                                type = leftType.getCompatibleType(rightType);
1534:                            }
1535:                        } else {
1536:                            type = leftType.getCompatibleType(rightType);
1537:                        }
1538:
1539:                        if (type == null) {
1540:                            type = Type.NULL_TYPE;
1541:                        }
1542:
1543:                        Class clazz = type.getObjectClass();
1544:
1545:                        if (type.hasPrimitivePeer()
1546:                                && leftType.isNonNull()
1547:                                && rightType.isNonNull()
1548:                                && (leftType.isPrimitive() || leftType
1549:                                        .hasPrimitivePeer())
1550:                                && (rightType.isPrimitive() || rightType
1551:                                        .hasPrimitivePeer())) {
1552:                            leftType = rightType = type.toPrimitive();
1553:                        } else {
1554:                            if (leftType.isNonNull()) {
1555:                                leftType = type.toNonNull();
1556:                                if (rightType.isNonNull()) {
1557:                                    rightType = leftType;
1558:                                } else {
1559:                                    rightType = type;
1560:                                }
1561:                            } else {
1562:                                leftType = type;
1563:                                if (rightType.isNonNull()) {
1564:                                    rightType = type.toNonNull();
1565:                                } else {
1566:                                    rightType = type;
1567:                                }
1568:                            }
1569:                        }
1570:
1571:                        if (ID == Token.EQ || ID == Token.NE
1572:                                || Comparable.class.isAssignableFrom(clazz)
1573:                                || String.class.isAssignableFrom(clazz)
1574:                                || Number.class.isAssignableFrom(clazz)) {
1575:
1576:                            // Don't prefer cast; possibly perform string conversion.
1577:                            left.convertTo(leftType, false);
1578:                            right.convertTo(rightType, false);
1579:                        } else {
1580:                            error("relationalexpression.type.mismatch", token
1581:                                    .getImage(),
1582:                                    left.getType().getSimpleName(), right
1583:                                            .getType().getSimpleName(), node);
1584:                        }
1585:                    }
1586:
1587:                    node.setType(Type.BOOLEAN_TYPE);
1588:
1589:                    return null;
1590:                }
1591:
1592:                private Object visitIsa(RelationalExpression node) {
1593:                    Token token = node.getOperator();
1594:
1595:                    Expression left = node.getLeftExpression();
1596:                    TypeName typeName = node.getIsaTypeName();
1597:
1598:                    check(left);
1599:                    check(typeName);
1600:
1601:                    if (!(left instanceof  VariableRef)) {
1602:                        error("relationalexpression.isa.left",
1603:                                token.getImage(), left);
1604:                    }
1605:
1606:                    Type leftType = left.getType();
1607:                    Type rightType = typeName.getType();
1608:
1609:                    if (leftType != null && rightType != null) {
1610:                        // Ensure the left type is an object.
1611:                        leftType = leftType.toNonPrimitive();
1612:                        left.convertTo(leftType);
1613:
1614:                        Class leftClass = leftType.getObjectClass();
1615:                        Class rightClass = rightType.getObjectClass();
1616:
1617:                        if (rightClass.isAssignableFrom(leftClass)) {
1618:                            // Widening case. i.e. (5 isa Number) is always true.
1619:                        } else if (leftClass.isAssignableFrom(rightClass)) {
1620:                            // Narrowing case. i.e. (n isa Integer) might be true.
1621:
1622:                            // For this case, a cast operation needs to be inserted
1623:                            // in an if statement.
1624:                        } else {
1625:                            SourceInfo info = new SourceDetailedInfo(node
1626:                                    .getSourceInfo(), typeName.getSourceInfo()
1627:                                    .getDetailPosition());
1628:
1629:                            error("relationalexpression.isa.impossible",
1630:                                    leftType.getSimpleName(), rightType
1631:                                            .getSimpleName(), info);
1632:                        }
1633:                    }
1634:
1635:                    return null;
1636:                }
1637:
1638:                public Object visit(AndExpression node) {
1639:                    Expression left = node.getLeftExpression();
1640:                    Expression right = node.getRightExpression();
1641:
1642:                    check(left);
1643:                    check(right);
1644:
1645:                    Type type = Type.BOOLEAN_TYPE;
1646:
1647:                    if (binaryTypeCheck(node, Boolean.class)) {
1648:                        left.convertTo(type);
1649:                        right.convertTo(type);
1650:                    }
1651:
1652:                    node.setType(type);
1653:
1654:                    return null;
1655:                }
1656:
1657:                public Object visit(OrExpression node) {
1658:                    Expression left = node.getLeftExpression();
1659:                    Expression right = node.getRightExpression();
1660:
1661:                    check(left);
1662:                    check(right);
1663:
1664:                    Type type = Type.BOOLEAN_TYPE;
1665:
1666:                    if (binaryTypeCheck(node, Boolean.class)) {
1667:                        left.convertTo(type);
1668:                        right.convertTo(type);
1669:                    }
1670:
1671:                    node.setType(type);
1672:
1673:                    return null;
1674:                }
1675:
1676:                public Object visit(NullLiteral node) {
1677:                    return null;
1678:                }
1679:
1680:                public Object visit(BooleanLiteral node) {
1681:                    return null;
1682:                }
1683:
1684:                public Object visit(StringLiteral node) {
1685:                    return null;
1686:                }
1687:
1688:                public Object visit(NumberLiteral node) {
1689:                    return null;
1690:                }
1691:            }
1692:
1693:            /**
1694:             * Detects relational expressions that refine variable types for an if
1695:             * statement's then and else scopes.
1696:             */
1697:            private static class IsaDetector extends TreeWalker {
1698:                private static final char NOT_OP = 'n';
1699:                private static final char AND_OP = 'a';
1700:                private static final char OR_OP = 'o';
1701:
1702:                private StringBuffer mOpStack;
1703:
1704:                private Collection mThenCasts;
1705:                private Collection mElseCasts;
1706:
1707:                public Variable[] getThenCasts() {
1708:                    if (mThenCasts == null) {
1709:                        return null;
1710:                    } else {
1711:                        Variable[] vars = new Variable[mThenCasts.size()];
1712:                        return (Variable[]) mThenCasts.toArray(vars);
1713:                    }
1714:                }
1715:
1716:                public Variable[] getElseCasts() {
1717:                    if (mElseCasts == null) {
1718:                        return null;
1719:                    } else {
1720:                        Variable[] vars = new Variable[mElseCasts.size()];
1721:                        return (Variable[]) mElseCasts.toArray(vars);
1722:                    }
1723:                }
1724:
1725:                public Object visit(RelationalExpression node) {
1726:                    boolean typeKnown = true;
1727:                    boolean forThenPart = true;
1728:
1729:                    if (mOpStack != null) {
1730:                        int length = mOpStack.length();
1731:                        for (int i = 0; i < length; i++) {
1732:                            switch (mOpStack.charAt(i)) {
1733:                            case NOT_OP:
1734:                                forThenPart = !forThenPart;
1735:                                break;
1736:                            case AND_OP:
1737:                                if (!forThenPart) {
1738:                                    typeKnown = false;
1739:                                }
1740:                                break;
1741:                            case OR_OP:
1742:                                if (forThenPart) {
1743:                                    typeKnown = false;
1744:                                }
1745:                                break;
1746:                            }
1747:                        }
1748:                    }
1749:
1750:                    if (!typeKnown) {
1751:                        return super .visit(node);
1752:                    }
1753:
1754:                    int operator = node.getOperator().getID();
1755:
1756:                    if (operator == Token.ISA) {
1757:                        TypeName typeName = node.getIsaTypeName();
1758:                        if (typeName != null) {
1759:                            Expression left = node.getLeftExpression();
1760:                            if (left instanceof  VariableRef) {
1761:                                Type type = typeName.getType();
1762:                                if (type != null) {
1763:                                    addCast((VariableRef) left, type
1764:                                            .toNonNull(), forThenPart);
1765:                                }
1766:                            }
1767:                        }
1768:                    } else if (operator == Token.EQ || operator == Token.NE) {
1769:                        if (operator == Token.EQ) {
1770:                            forThenPart = !forThenPart;
1771:                        }
1772:
1773:                        VariableRef ref;
1774:                        Expression test;
1775:
1776:                        Expression left = node.getLeftExpression();
1777:                        Expression right = node.getRightExpression();
1778:
1779:                        if (left instanceof  VariableRef) {
1780:                            ref = (VariableRef) left;
1781:                            test = right;
1782:                        } else if (right instanceof  VariableRef) {
1783:                            ref = (VariableRef) right;
1784:                            test = left;
1785:                        } else {
1786:                            ref = null;
1787:                            test = null;
1788:                        }
1789:
1790:                        if (test != null && test.isValueKnown()
1791:                                && test.getValue() == null) {
1792:
1793:                            Type type = ref.getType();
1794:                            if (type != null) {
1795:                                // If the expression "var != null" is true, then
1796:                                // the the type becomes non-null.
1797:
1798:                                if (!type.isNonNull()) {
1799:                                    addCast(ref, type.toNonNull(), forThenPart);
1800:                                }
1801:                            }
1802:                        }
1803:                    }
1804:
1805:                    return super .visit(node);
1806:                }
1807:
1808:                public Object visit(NotExpression node) {
1809:                    pushOp(NOT_OP);
1810:                    super .visit(node);
1811:                    popOp();
1812:                    return null;
1813:                }
1814:
1815:                public Object visit(AndExpression node) {
1816:                    pushOp(AND_OP);
1817:                    super .visit(node);
1818:                    popOp();
1819:                    return null;
1820:                }
1821:
1822:                public Object visit(OrExpression node) {
1823:                    pushOp(OR_OP);
1824:                    super .visit(node);
1825:                    popOp();
1826:                    return null;
1827:                }
1828:
1829:                private void pushOp(char op) {
1830:                    if (mOpStack == null) {
1831:                        mOpStack = new StringBuffer(4);
1832:                    }
1833:                    mOpStack.append(op);
1834:                }
1835:
1836:                private void popOp() {
1837:                    if (mOpStack != null) {
1838:                        int length = mOpStack.length();
1839:                        if (length > 0) {
1840:                            mOpStack.setLength(length - 1);
1841:                        }
1842:                    }
1843:                }
1844:
1845:                private void addCast(VariableRef ref, Type type,
1846:                        boolean forThenPart) {
1847:                    Variable oldVar = ref.getVariable();
1848:                    if (oldVar != null) {
1849:                        Variable newVar = (Variable) oldVar.clone();
1850:                        newVar.setType(type);
1851:                        newVar.setField(false);
1852:
1853:                        if (forThenPart) {
1854:                            if (mThenCasts == null) {
1855:                                mThenCasts = new ArrayList(2);
1856:                            }
1857:                            mThenCasts.add(newVar);
1858:                        } else {
1859:                            if (mElseCasts == null) {
1860:                                mElseCasts = new ArrayList(2);
1861:                            }
1862:                            mElseCasts.add(newVar);
1863:                        }
1864:                    }
1865:                }
1866:            }
1867:
1868:            /**
1869:             * Ensures that the template ends in a ReturnStatement. If the final
1870:             * Statement in the template is an ExpressionStatement, then it is 
1871:             * converted to a ReturnStatement. Otherwise, a void ReturnStatement is
1872:             * added to the end.
1873:             */
1874:            private static class ReturnConvertor extends TreeMutator {
1875:                private boolean mReturnAdded;
1876:
1877:                public Object visit(Template node) {
1878:                    Statement stmt = node.getStatement();
1879:                    if (stmt != null) {
1880:                        stmt = (Statement) stmt.accept(this );
1881:                        if (!mReturnAdded) {
1882:                            Statement[] stmts = new Statement[] { stmt,
1883:                                    new ReturnStatement(stmt.getSourceInfo()) };
1884:                            stmt = new StatementList(stmt.getSourceInfo(),
1885:                                    stmts);
1886:                        }
1887:                        node.setStatement(stmt);
1888:                    } else {
1889:                        node.setStatement(new ReturnStatement(node
1890:                                .getSourceInfo()));
1891:                    }
1892:
1893:                    return node;
1894:                }
1895:
1896:                public Object visit(Statement node) {
1897:                    return node;
1898:                }
1899:
1900:                public Object visit(StatementList node) {
1901:                    // Just traverse the last statement in the list.
1902:                    Statement[] statements = node.getStatements();
1903:                    for (int i = statements.length - 1; i >= 0; i--) {
1904:                        Statement stmt = statements[i];
1905:                        if (stmt != null) {
1906:                            statements[i] = (Statement) stmt.accept(this );
1907:                            break;
1908:                        }
1909:                    }
1910:                    return node;
1911:                }
1912:
1913:                public Object visit(Block node) {
1914:                    return visit((StatementList) node);
1915:                }
1916:
1917:                public Object visit(ExpressionStatement node) {
1918:                    mReturnAdded = true;
1919:                    Expression expr = node.getExpression();
1920:                    if (expr instanceof  CallExpression) {
1921:                        ((CallExpression) expr).setVoidPermitted(true);
1922:                    }
1923:                    return new ReturnStatement(expr);
1924:                }
1925:
1926:                public Object visit(AssignmentStatement node) {
1927:                    // Skip traversing this node altogether.
1928:                    return node;
1929:                }
1930:
1931:                public Object visit(BreakStatement node) {
1932:                    // Skip traversing this node altogether.
1933:                    return node;
1934:                }
1935:
1936:                public Object visit(ForeachStatement node) {
1937:                    // Skip traversing this node altogether.
1938:                    return node;
1939:                }
1940:
1941:                public Object visit(IfStatement node) {
1942:                    // Skip traversing this node altogether.
1943:                    return node;
1944:                }
1945:
1946:                public Object visit(SubstitutionStatement node) {
1947:                    // Skip traversing this node altogether.
1948:                    return node;
1949:                }
1950:
1951:                public Object visit(ReturnStatement node) {
1952:                    // Skip traversing this node altogether.
1953:                    return node;
1954:                }
1955:            }
1956:
1957:            /**
1958:             * Wraps all statements that can throw an exception with an
1959:             * ExceptionGuardStatement.
1960:             */
1961:            private static class ExceptionGuardian extends TreeMutator {
1962:                public Object visit(AssignmentStatement node) {
1963:                    if (!node.getRValue().isExceptionPossible()) {
1964:                        return node;
1965:                    }
1966:
1967:                    SourceInfo info = node.getSourceInfo();
1968:                    VariableRef lvalue = node.getLValue();
1969:
1970:                    // Replacement assigns null to lvalue.
1971:                    Statement replacement = new AssignmentStatement(info,
1972:                            lvalue, new NullLiteral(info));
1973:
1974:                    return new ExceptionGuardStatement(node, replacement);
1975:                }
1976:
1977:                public Object visit(ForeachStatement node) {
1978:                    Block body = node.getBody();
1979:                    if (body != null) {
1980:                        node.setBody(visitBlock(body));
1981:                    }
1982:
1983:                    boolean guard = false;
1984:                    test: {
1985:                        Expression range = node.getRange();
1986:                        if (range != null) {
1987:                            if (range.isExceptionPossible()) {
1988:                                guard = true;
1989:                                break test;
1990:                            }
1991:                            Type type = range.getType();
1992:                            if (type != null && type.isNullable()) {
1993:                                guard = true;
1994:                                break test;
1995:                            }
1996:                        }
1997:
1998:                        range = node.getEndRange();
1999:                        if (range != null) {
2000:                            if (range.isExceptionPossible()) {
2001:                                guard = true;
2002:                                break test;
2003:                            }
2004:                            Type type = range.getType();
2005:                            if (type != null && type.isNullable()) {
2006:                                guard = true;
2007:                                break test;
2008:                            }
2009:                        }
2010:                    }
2011:
2012:                    if (!guard) {
2013:                        return node;
2014:                    }
2015:
2016:                    return new ExceptionGuardStatement(node, node
2017:                            .getInitializer());
2018:                }
2019:
2020:                public Object visit(IfStatement node) {
2021:                    Block block = node.getThenPart();
2022:                    if (block != null) {
2023:                        node.setThenPart(visitBlock(block));
2024:                    }
2025:
2026:                    block = node.getElsePart();
2027:                    if (block != null) {
2028:                        node.setElsePart(visitBlock(block));
2029:                    }
2030:
2031:                    if (!node.getCondition().isExceptionPossible()) {
2032:                        return node;
2033:                    }
2034:
2035:                    Variable[] vars = node.getMergedVariables();
2036:                    int length;
2037:
2038:                    if (vars == null || (length = vars.length) == 0) {
2039:                        return new ExceptionGuardStatement(node, null);
2040:                    }
2041:
2042:                    // Create replacement assignments to ensure merged variables get
2043:                    // assigned null.
2044:                    Statement[] replacements = new Statement[length];
2045:                    SourceInfo info = node.getSourceInfo();
2046:
2047:                    for (int i = 0; i < length; i++) {
2048:                        Variable v = vars[i];
2049:                        VariableRef lvalue = new VariableRef(v.getSourceInfo(),
2050:                                v.getName());
2051:                        lvalue.setVariable(v);
2052:
2053:                        replacements[i] = new AssignmentStatement(info, lvalue,
2054:                                new NullLiteral(info));
2055:                    }
2056:
2057:                    Statement replacement = new StatementList(info,
2058:                            replacements);
2059:                    return new ExceptionGuardStatement(node, replacement);
2060:                }
2061:
2062:                public Object visit(SubstitutionStatement node) {
2063:                    return new ExceptionGuardStatement(node, null);
2064:                }
2065:
2066:                public Object visit(ExpressionStatement node) {
2067:                    Expression expr = node.getExpression();
2068:                    if (expr != null) {
2069:                        if (expr instanceof  CallExpression) {
2070:                            expr = (CallExpression) expr.accept(this );
2071:                            node.setExpression(expr);
2072:                        }
2073:                        if (expr.isExceptionPossible()) {
2074:                            return new ExceptionGuardStatement(node, null);
2075:                        }
2076:                    }
2077:                    return node;
2078:                }
2079:
2080:                public Object visit(ReturnStatement node) {
2081:                    Expression expr = node.getExpression();
2082:                    if (expr instanceof  CallExpression) {
2083:                        node.setExpression((CallExpression) expr.accept(this ));
2084:                    }
2085:                    return node;
2086:                }
2087:
2088:                public Object visit(FunctionCallExpression node) {
2089:                    return visit((CallExpression) node);
2090:                }
2091:
2092:                public Object visit(TemplateCallExpression node) {
2093:                    return visit((CallExpression) node);
2094:                }
2095:
2096:                private Object visit(CallExpression node) {
2097:                    Block subParam = node.getSubstitutionParam();
2098:                    if (subParam != null) {
2099:                        node.setSubstitutionParam(visitBlock(subParam));
2100:                    }
2101:                    return node;
2102:                }
2103:            }
2104:
2105:            /**
2106:             * Reduces the amount of string concatenation operations performed by
2107:             * breaking up ExpressionStatements that encapsulate ConcatenateExpressions
2108:             * into several individual ExpressionStatements. Concatenations that are
2109:             * enclosed in parenthesis are not broken up.
2110:             */
2111:            private static class ConcatenationReducer extends TreeMutator {
2112:                public Object visit(ExpressionStatement node) {
2113:                    // Recurse into node.
2114:                    super .visit(node);
2115:
2116:                    Expression expr = node.getExpression();
2117:                    if (!(expr instanceof  ConcatenateExpression)) {
2118:                        return node;
2119:                    }
2120:
2121:                    Collection statements = new ArrayList();
2122:                    breakup(statements, (ConcatenateExpression) expr);
2123:
2124:                    Statement[] stmts = new Statement[statements.size()];
2125:                    stmts = (Statement[]) statements.toArray(stmts);
2126:
2127:                    return new StatementList(node.getSourceInfo(), stmts);
2128:                }
2129:
2130:                private void breakup(Collection statements,
2131:                        ConcatenateExpression concat) {
2132:
2133:                    Expression left = concat.getLeftExpression();
2134:                    if (left instanceof  ConcatenateExpression) {
2135:                        breakup(statements, ((ConcatenateExpression) left));
2136:                    } else {
2137:                        statements.add(new ExpressionStatement(left));
2138:                    }
2139:
2140:                    Expression right = concat.getRightExpression();
2141:                    if (right instanceof  ConcatenateExpression) {
2142:                        breakup(statements, ((ConcatenateExpression) right));
2143:                    } else {
2144:                        statements.add(new ExpressionStatement(right));
2145:                    }
2146:                }
2147:            }
2148:
2149:            /**
2150:             * Increases the amount of string concatenation operations by finding
2151:             * consecutive ExpressionStatements and merging them together into a
2152:             * single ExpressionStatement that operates on the concatenated results.
2153:             */
2154:            private static class ConcatenationIncreaser extends TreeMutator {
2155:                public Object visit(StatementList node) {
2156:                    // Recurse into node.
2157:                    super .visit(node);
2158:
2159:                    return new StatementList(node.getSourceInfo(), visit(node
2160:                            .getStatements()));
2161:                }
2162:
2163:                public Object visit(Block node) {
2164:                    // Recurse into node.
2165:                    super .visit(node);
2166:
2167:                    return new Block(node.getSourceInfo(), visit(node
2168:                            .getStatements()));
2169:                }
2170:
2171:                private Statement[] visit(Statement[] stmts) {
2172:                    int length = stmts.length;
2173:
2174:                    Collection statements = new ArrayList();
2175:                    List expressionStatements = new ArrayList();
2176:
2177:                    for (int i = 0; i < length; i++) {
2178:                        Statement stmt = stmts[i];
2179:                        if (stmt instanceof  ExpressionStatement) {
2180:                            Expression expr = ((ExpressionStatement) stmt)
2181:                                    .getExpression();
2182:
2183:                            if (!(expr instanceof  CallExpression)) {
2184:                                expressionStatements.add(stmt);
2185:                                continue;
2186:                            }
2187:                        }
2188:
2189:                        merge(statements, expressionStatements);
2190:                        expressionStatements.clear();
2191:                        statements.add(stmt);
2192:                    }
2193:
2194:                    merge(statements, expressionStatements);
2195:
2196:                    stmts = new Statement[statements.size()];
2197:                    return (Statement[]) statements.toArray(stmts);
2198:                }
2199:
2200:                private void merge(Collection statements,
2201:                        List expressionStatements) {
2202:                    int size = expressionStatements.size();
2203:
2204:                    if (size == 0) {
2205:                    } else if (size == 1) {
2206:                        statements.add(expressionStatements.get(0));
2207:                    } else {
2208:                        List expressions = new ArrayList();
2209:
2210:                        for (int i = 0; i < size; i++) {
2211:                            ExpressionStatement stmt = (ExpressionStatement) expressionStatements
2212:                                    .get(i);
2213:                            gatherExpressions(expressions, stmt.getExpression());
2214:                        }
2215:
2216:                        size = expressions.size();
2217:                        Expression concat = (Expression) expressions.get(0);
2218:
2219:                        for (int i = 1; i < size; i++) {
2220:                            Expression right = (Expression) expressions.get(i);
2221:                            SourceInfo rightInfo = right.getSourceInfo();
2222:
2223:                            SourceInfo info = concat.getSourceInfo()
2224:                                    .setEndPosition(rightInfo);
2225:
2226:                            Token token = new Token(rightInfo, Token.CONCAT);
2227:
2228:                            concat = new ConcatenateExpression(info, token,
2229:                                    concat, right);
2230:                        }
2231:
2232:                        statements.add(new ExpressionStatement(concat));
2233:                    }
2234:                }
2235:
2236:                private void gatherExpressions(Collection expressions,
2237:                        Expression expr) {
2238:                    if (expr instanceof  ConcatenateExpression) {
2239:                        ConcatenateExpression concat = (ConcatenateExpression) expr;
2240:                        gatherExpressions(expressions, concat
2241:                                .getLeftExpression());
2242:                        gatherExpressions(expressions, concat
2243:                                .getRightExpression());
2244:                    } else {
2245:                        expressions.add(expr);
2246:                    }
2247:                }
2248:            }
2249:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.