001    /*BEGIN_COPYRIGHT_BLOCK
002     *
003     * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
004     * All rights reserved.
005     * 
006     * Redistribution and use in source and binary forms, with or without
007     * modification, are permitted provided that the following conditions are met:
008     *    * Redistributions of source code must retain the above copyright
009     *      notice, this list of conditions and the following disclaimer.
010     *    * Redistributions in binary form must reproduce the above copyright
011     *      notice, this list of conditions and the following disclaimer in the
012     *      documentation and/or other materials provided with the distribution.
013     *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014     *      names of its contributors may be used to endorse or promote products
015     *      derived from this software without specific prior written permission.
016     * 
017     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025     * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026     * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028     *
029     * This software is Open Source Initiative approved Open Source Software.
030     * Open Source Initative Approved is a trademark of the Open Source Initiative.
031     * 
032     * This file is part of DrJava.  Download the current version of this project
033     * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034     * 
035     * END_COPYRIGHT_BLOCK*/
036    
037    package edu.rice.cs.javalanglevels;
038    
039    import edu.rice.cs.javalanglevels.tree.*;
040    import edu.rice.cs.javalanglevels.parser.*;
041    import edu.rice.cs.javalanglevels.util.*;
042    import java.util.*;
043    import java.io.*;
044    
045    import junit.framework.TestCase;
046    
047    /** Language Level Visitor for the Intermediate Language Level.  Enforces constraints during the
048      * first walk of the AST (checking for language specific errors and building the symbol table).
049      * This class enforces things that are common to all contexts reachable within a method body or other body 
050      * (not class or interface body) at the Intermediate Language Level). 
051      */
052    public class BodyBodyIntermediateVisitor extends IntermediateVisitor {
053    
054      /**The MethodData of this method.*/
055      private BodyData _bodyData;
056      
057      /** Constructor for BodyBodyElementaryVisitor.
058        * @param bodyData  The BodyData that encloses the context we are visiting.
059        * @param file  The source file this came from.
060        * @param packageName  The package the source file is in
061        * @importedFiles  A list of classes that were specifically imported
062        * @param importedPackages  A list of package names that were specifically imported
063        * @param classesInThisFile  A list of the classes that are yet to be defined in this source file
064        * @param continuations  A hashtable corresponding to the continuations (unresolved Symbol Datas) that will need 
065        *                       to be resolved
066        * @param fixUps  A list of commands to be performed after this pass to fixup the symbolTable
067        */
068      public BodyBodyIntermediateVisitor(BodyData bodyData,
069                                         File file, 
070                                         String packageName, 
071                                         String enclosingClassName,
072                                         LinkedList<String> importedFiles, 
073                                         LinkedList<String> importedPackages,
074                                         HashSet<String> classesInThisFile, 
075                                         Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations,
076                                         LinkedList<Command> fixUps,
077                                         HashSet<String> innerClassesInThisBody) {
078        super(file, 
079              packageName,
080              enclosingClassName,
081              importedFiles, 
082              importedPackages, 
083              classesInThisFile, 
084              continuations,
085              fixUps);
086        _bodyData = bodyData;
087        assert _enclosingClassName != null;
088    //    _innerClassesInThisBody = innerClassesInThisBody;
089      }
090      
091      /** Give an appropriate error */
092      public Void forMethodDefDoFirst(MethodDef that) {
093        _addError("Methods definitions cannot appear within the body of another method or block.", that);
094        return null;
095      }
096      
097      /* There is currently no way to differentiate between a block statement and
098       * an instance initializer in a braced body given the general nature of a 
099       * braced body.  Whenever an instance initialization is visited in a method
100       * body, we must assume that it is a block statement.
101       */
102      public Void forInstanceInitializer(InstanceInitializer that) {
103        return forBlock(that.getCode());
104      }
105    
106     /* Visit this Block within the same BlockData with a new BodyBodyIntermediate visitor (to deal with new local scope?)
107      * after making sure no errors need to be thrown.*/
108      public Void forBlock(Block that) {
109        forBlockDoFirst(that);
110        if (prune(that)) return null;
111        // The following code is incomprehensible.  Why mutate _bodyData?  Why create bd?
112        BlockData bd = new BlockData(_bodyData);
113        _bodyData.addBlock(bd);
114        that.getStatements().visit(new BodyBodyIntermediateVisitor(bd, _file, _package, _enclosingClassName, _importedFiles,
115                                                                   _importedPackages, _classesInThisFile, continuations, 
116                                                                   fixUps, new HashSet<String>()));
117        return forBlockOnly(that);
118      }
119      
120      /** Visit the block as in forBlock(), but first add the exception parameter as a variable in that 
121        * lock. 
122        * TODO: move this method into LanguageLevelVisitor. */
123      public Void forCatchBlock(CatchBlock that) {
124    //    System.err.println("***ALARM*** BodyBodyIntermediateVisitor is visiting catch block");
125        forCatchBlockDoFirst(that);
126        if (prune(that)) return null;
127        
128        Block b = that.getBlock();
129        forBlockDoFirst(b);
130        if (prune(b)) return null;
131        BlockData bd = new BlockData(_bodyData);
132        _bodyData.addBlock(bd);
133        
134        SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
135       
136        VariableData exceptionVar = 
137          formalParameters2VariableData(new FormalParameter[]{ that.getException() }, enclosing)[0];  // !!!!!! Wny not bd?
138        if (prune(that.getException())) return null;
139        bd.addVar(exceptionVar);
140        
141    //    System.err.println("Visiting augmented catch block with new visitor!");
142        BodyBodyIntermediateVisitor bbijv = 
143          new BodyBodyIntermediateVisitor(bd, _file, _package, _enclosingClassName, _importedFiles,
144                                          _importedPackages, _classesInThisFile, continuations, fixUps,
145                                          new HashSet<String>());
146        b.getStatements().visit(bbijv);
147        forBlockOnly(b);
148        return forCatchBlockOnly(that);
149      }
150      
151      /** Adds the variables that were declared to the body data and make sure that no two variables have the same name.*/
152      public Void forVariableDeclarationOnly(VariableDeclaration that) {
153        if (! _bodyData.addFinalVars(_variableDeclaration2VariableData(that, _bodyData))) {
154          /* Does not allow repeated use of a for loop index variable.  TODO: fix this. */
155    //      System.err.println("Generating duplicate variable error");
156          _addAndIgnoreError("You cannot have two variables with the same name.", that);
157        }
158        return null;
159      }
160      
161    //  /** Override method in IntermediateVisitor that throws an error here.*/
162    //  public Void forTryCatchStatementDoFirst(TryCatchStatement that) { return null; /*  No errors to throw here. */ }
163      
164      /** Process a local inner class definition */
165      public Void forInnerClassDef(InnerClassDef that) {
166        // TODO: is this necessarily local?
167        SymbolData enclosingClass = _bodyData.getSymbolData();
168        assert _enclosingClassName != null;
169        assert enclosingClass != null;
170        assert _enclosingClassName.equals(getQualifiedClassName(enclosingClass.getName()));
171        
172        String relName = that.getName().getText();
173        String fullName = _enclosingClassName + '$' + enclosingClass.preincrementLocalClassNum() + relName;
174    //    System.err.println("***ALARM*** Processing local class '" + relName + "' in class " + _enclosingClassName
175    //                         + " with flattened class name " + fullName);
176        handleInnerClassDef(that, _bodyData, relName, fullName);
177        // How do we know that generated number is correct?
178        return null;
179      }
180      
181      /** Process a local inner interface definition */
182      public Void forInnerInterfaceDef(InnerInterfaceDef that) {
183    //    System.err.println("***Signalling local interface error");
184        _addAndIgnoreError("Local interfaces are illegal in Java.", that);
185    //    handleInnerInterfaceDef(that, _bodyData, getQualifiedClassName(_bodyData.getSymbolData().getName()) + '.' + 
186    //                        _bodyData.getSymbolData().preincrementLocalClassNum() + that.getName().getText());
187    //    // How do we know that generated number is correct?
188        return null;
189      }
190    
191      /** Delegate to helper method. */
192      public Void forSimpleAnonymousClassInstantiation(SimpleAnonymousClassInstantiation that) {
193        SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
194        assert enclosing != null;
195        simpleAnonymousClassInstantiationHelper(that, enclosing);
196        return null;
197      }
198      
199      /** Delegate to helper method. */
200      public Void forComplexAnonymousClassInstantiation(ComplexAnonymousClassInstantiation that) {
201        SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
202        assert enclosing != null;
203        complexAnonymousClassInstantiationHelper(that, enclosing);  // TODO: the wrong enclosing context?
204        return null;
205      }
206      
207      /** If this is the body of a constructor, referencing 'this' is illegal. So, check to see if this is a constructor,
208        * and if so, throw an error. This should catch both the ComplexThisReference and the SimpleThisReference case.
209        */
210      //TODO: it might be nice to create a ConstructorBodyIntermediateVisitor, so this check is not necessary here.
211      public Void forThisReferenceDoFirst(ThisReference that) {
212        if (isConstructor(_bodyData)) {
213          _addAndIgnoreError("You cannot reference the field 'this' inside a constructor at the Intermediate Level", that);
214        }
215        return null;
216      }
217    
218      /** Process a local variable declaration within a method.  Calls the super method to convert these to a VariableData
219        * array, then makes sure that each VariableData is set to be final, as required at the Intermediate level.
220        * @param enclosingData  The Data which contains the variables
221        */
222      protected VariableData[] _variableDeclaration2VariableData(VariableDeclaration vd, Data enclosingData) {
223        VariableData[] vds = super._variableDeclaration2VariableData(vd, enclosingData);
224        for (int i = 0; i < vds.length; i++) {
225          if (vds[i].getMav().getModifiers().length > 0) {
226            StringBuilder s = new StringBuilder("the keyword(s) ");
227            String[] modifiers = vds[i].getMav().getModifiers();
228            for (String m: modifiers) { if (! m.equals("final")) s.append("\"" + m + "\" "); }
229            _addAndIgnoreError("You cannot use " + s + "to declare a local variable", vd);
230          }
231          if (! vds[i].isFinal()) vds[i].setFinal();
232          vds[i].setIsLocalVariable(true);  // Was commented out.  Why ????
233        }
234    //    System.err.println("Return VariableDatas " + vds);
235        return vds;
236      }
237      
238    //  /** Check for problems with modifiers that are specific to method definitions. */
239    //  public Void forModifiersAndVisibilityDoFirst(ModifiersAndVisibility that) {
240    //    String[] modifiers = that.getModifiers();
241    //    if (Utilities.isAbstract(modifiers) && Utilities.isStatic(modifiers))  _badModifiers("static", "abstract", that);
242    //    return super.forModifiersAndVisibilityDoFirst(that);
243    //  }
244      
245      /** Test most of the methods declared above right here: */
246      public static class BodyBodyIntermediateVisitorTest extends TestCase {
247        
248        private BodyBodyIntermediateVisitor _bbv;
249        
250        private SymbolData _sd1;
251        private MethodData _md1;
252        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
253        private ModifiersAndVisibility _protectedMav =
254          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
255        private ModifiersAndVisibility _privateMav = 
256          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
257        private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
258        private ModifiersAndVisibility _abstractMav = 
259          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
260        private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
261        
262        
263        public BodyBodyIntermediateVisitorTest() { this(""); }
264        
265        public BodyBodyIntermediateVisitorTest(String name) { super(name); }
266        
267        public void setUp() {
268          _sd1 = new SymbolData("ILikeMonkey");
269          _sd1.setIsContinuation(false);
270          
271          _md1 = new MethodData("methodName", 
272                                _publicMav, 
273                                new TypeParameter[0], 
274                                SymbolData.INT_TYPE, 
275                                new VariableData[0], 
276                                new String[0],
277                                _sd1,
278                                null);
279    
280          errors = new LinkedList<Pair<String, JExpressionIF>>();
281          LanguageLevelConverter.symbolTable.clear();
282          LanguageLevelConverter._newSDs.clear();
283          LanguageLevelConverter.symbolTable.put("ILikeMonkey", _sd1);
284          visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();      
285    //      _hierarchy = new Hashtable<String, TypeDefBase>();
286          
287          _bbv = 
288            new BodyBodyIntermediateVisitor(_md1, 
289                                            new File(""), 
290                                            "", 
291                                            "ILikeMonkey", 
292                                            new LinkedList<String>(), 
293                                            new LinkedList<String>(), 
294                                            new HashSet<String>(), 
295                                            new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
296                                            new LinkedList<Command>(),
297                                            new HashSet<String>());
298          
299          _bbv._classesInThisFile = new HashSet<String>();
300          _bbv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
301    //      _bbv._resetNonStaticFields();
302          _bbv._importedPackages.addFirst("java.lang");
303          _sd1.setSuperClass(_bbv.getQualifiedSymbolData("java.lang.Object"));
304    
305          _errorAdded = false;
306        }
307        
308        public void testForMethodDefDoFirst() {
309          ConcreteMethodDef cmd = new ConcreteMethodDef(SourceInfo.NONE, 
310                                                        _packageMav, 
311                                                        new TypeParameter[0], 
312                                                        new PrimitiveType(SourceInfo.NONE, "int"), 
313                                                        new Word(SourceInfo.NONE, "methodName"),
314                                                        new FormalParameter[0],
315                                                        new ReferenceType[0], 
316                                                        new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
317          cmd.visit(_bbv);
318          assertEquals("There should be one error.", 1, errors.size());
319          assertEquals("The error message should be correct.", 
320                       "Methods definitions cannot appear within the body of another method or block.",
321                       errors.get(0).getFirst());
322        }
323        
324        /* These last two tests are shared with ClassBodyIntermediateVisitor, perhaps we could factor them out. */
325        
326        public void testForVariableDeclarationOnly() {
327          // Check one that works
328          VariableDeclaration vdecl = new VariableDeclaration(SourceInfo.NONE,
329                                                           _packageMav,
330                                                           new VariableDeclarator[] {
331            new UninitializedVariableDeclarator(SourceInfo.NONE, 
332                                   new PrimitiveType(SourceInfo.NONE, "double"), 
333                                   new Word (SourceInfo.NONE, "field1")),
334            new UninitializedVariableDeclarator(SourceInfo.NONE, 
335                                   new PrimitiveType(SourceInfo.NONE, "boolean"), 
336                                   new Word (SourceInfo.NONE, "field2"))});
337          VariableData vd1 = new VariableData("field1", _finalMav, SymbolData.DOUBLE_TYPE, false, _bbv._bodyData);
338          VariableData vd2 = new VariableData("field2", _finalMav, SymbolData.BOOLEAN_TYPE, false, _bbv._bodyData);
339          vdecl.visit(_bbv);
340    //      if (errors.size() > 0) System.err.println("Error was:" + errors.get(0).getFirst());
341          assertEquals("There should not be any errors.", 0, errors.size());
342          assertTrue("field1 was added.", _md1.getVars().contains(vd1));
343          assertTrue("field2 was added.", _md1.getVars().contains(vd2));
344          
345          // Check one that doesn't work
346          VariableDeclaration vdecl2 = new VariableDeclaration(SourceInfo.NONE,
347                                                            _packageMav,
348                                                            new VariableDeclarator[] {
349            new UninitializedVariableDeclarator(SourceInfo.NONE, 
350                                                new PrimitiveType(SourceInfo.NONE, "double"), 
351                                                new Word (SourceInfo.NONE, "field3")),
352            new UninitializedVariableDeclarator(SourceInfo.NONE, 
353                                                new PrimitiveType(SourceInfo.NONE, "int"), 
354                                                new Word (SourceInfo.NONE, "field3"))});
355          VariableData vd3 = new VariableData("field3", _finalMav, SymbolData.DOUBLE_TYPE, false, _bbv._bodyData);
356          vdecl2.visit(_bbv);
357          assertEquals("There should be one error.", 1, errors.size());
358          assertEquals("The error message should be correct", "You cannot have two variables with the same name.", 
359                       errors.get(0).getFirst());
360          assertTrue("field3 was added.", _md1.getVars().contains(vd3));
361        }
362        
363    //    public void testForOtherExpressionOnly() {
364    //      // Test that if the OtherExpressino contains a Word, that the Word is resolved.
365    //      assertFalse("java.lang.System should not be in the symbolTable.", 
366    //                  LanguageLevelConverter.symbolTable.containsKey("java.lang.System"));
367    //      Expression ex = 
368    //        new Expression( SourceInfo.NONE,
369    //                       new ExpressionPiece[] { new OtherExpression(SourceInfo.NONE, 
370    //                                                                   new Word(SourceInfo.NONE, "System"))});
371    //      ex.visit(_bbv);
372    //////      System.out.println(errors.get(0).getFirst());
373    ////      for (int i = 0; i < errors.size(); i++)
374    ////        System.out.println(errors.get(i).getFirst());
375    //      assertEquals("There should not be any errors.", 0, errors.size());
376    //      assertTrue("java.lang.System should be in the symbolTable.", 
377    //                 LanguageLevelConverter.symbolTable.containsKey("java.lang.System"));
378    //    }
379        
380        /** Generate a block containing a bitwise or operation and the specified SourceInfo si.   Note: we need
381          * to generate error blocks with distinct literal data to avoid generating duplicate error messages
382          * which are suppressed in the cumulative errors table. */
383        private Block _generateErrorBlock(int litValue) {
384          final SourceInfo si = SourceInfo.NONE;
385          BracedBody errorBody = new BracedBody(si, new BodyItemI[] {
386            new ExpressionStatement(si, 
387                                    new BitwiseOrExpression(si, 
388                                                            new SimpleNameReference(si, 
389                                                                                    new Word(si, "i")), 
390                                                            new IntegerLiteral(si, litValue)))});
391          return new Block(si, errorBody);
392        }
393          
394          
395        public void testForTryCatchStatement() {
396          //Make sure that no error is thrown
397          BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
398          Block b = new Block(SourceInfo.NONE, emptyBody);
399    
400          NormalTryCatchStatement ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[0]);
401          TryCatchFinallyStatement tcfs = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], b);
402          ntcs.visit(_bbv);
403          tcfs.visit(_bbv);
404          assertEquals("After visiting both NormalTryCatchStatement and TryCatchFinallyStatement, there should be no " 
405                         + "errors", 0, errors.size());
406          
407    //      //make sure that if there is an error in one of the bodies, it is caught:
408    //      BracedBody errorBody = new BracedBody(SourceInfo.NONE, new BodyItemI[] {
409    //        new ExpressionStatement(SourceInfo.NONE, 
410    //                                new BitwiseOrExpression(SourceInfo.NONE, 
411    //                                                        new SimpleNameReference(SourceInfo.NONE, 
412    //                                                                                new Word(SourceInfo.NONE, "i")), 
413    //                                                        new IntegerLiteral(SourceInfo.NONE, 10)))});
414    //      Block errorBlock = new Block(SourceInfo.NONE, errorBody);
415          
416    //      assert ! SourceInfo.TEST_0.equals(SourceInfo.TEST_1);
417          ntcs = new NormalTryCatchStatement(SourceInfo.TEST_0, _generateErrorBlock(0), new CatchBlock[0]);
418          ntcs.visit(_bbv);
419          assertEquals("Should be one error", 1, errors.size());
420          assertEquals("Error message should be correct", 
421                       "Bitwise or expressions cannot be used in the functional language level.  " 
422                         + "Perhaps you meant to compare two values using regular or (||)", 
423                       errors.getLast().getFirst());
424          
425          //make sure that if there is an error in one of the catch statements, it is caught:
426          UninitializedVariableDeclarator uvd = 
427            new UninitializedVariableDeclarator(SourceInfo.TEST_1, 
428                                                new PrimitiveType(SourceInfo.TEST_1, "int"), 
429                                                new Word(SourceInfo.TEST_1, "i"));
430          FormalParameter fp = new FormalParameter(SourceInfo.TEST_1, uvd, false);
431    
432          tcfs = new TryCatchFinallyStatement(SourceInfo.TEST_1, b, new CatchBlock[] {
433            new CatchBlock(SourceInfo.TEST_1, fp, _generateErrorBlock(1))
434          }, b);
435          
436         assertEquals("Should be one error", 1, errors.size());
437         tcfs.visit(_bbv);
438         assertEquals("Should be two errors", 2, errors.size());
439         assertEquals("Error message should be correct", 
440                      "Bitwise or expressions cannot be used in the functional language level."
441                      + "  Perhaps you meant to compare two values using regular or (||)", 
442                      errors.getLast().getFirst());
443        }
444        
445        public void testForThisReferenceDoFirst() {
446          SimpleThisReference str = new SimpleThisReference(SourceInfo.NONE);
447          ComplexThisReference ctr = 
448            new ComplexThisReference(SourceInfo.NONE, 
449                                     new SimpleNameReference(SourceInfo.NONE, new Word(SourceInfo.NONE, "field")));
450    
451          //if a this reference occurs outside of a constructor, no error
452          _bbv._bodyData = _md1;
453          str.visit(_bbv);
454          ctr.visit(_bbv);
455          assertEquals("Should be no errors", 0, errors.size());
456               
457          
458          //if a this reference occurs in a constructor, give an error
459          MethodData constr = new MethodData("ILikeMonkey", _publicMav, new TypeParameter[0], _sd1, 
460                                       new VariableData[0], 
461                                       new String[0],
462                                       _sd1,
463                                       null);
464          _bbv._bodyData = constr;
465          str.visit(_bbv);
466          assertEquals("Should be 1 error", 1, errors.size());
467          assertEquals("Error message should be correct", 
468                       "You cannot reference the field 'this' inside a constructor at the Intermediate Level", 
469                       errors.getLast().getFirst());
470          
471          ctr.visit(_bbv);
472          assertEquals("Should be 2 errors", 2, errors.size());
473          assertEquals("Error message should be correct", 
474                       "You cannot reference the field 'this' inside a constructor at the Intermediate Level", 
475                       errors.getLast().getFirst());
476          
477          
478        }
479        
480         public void testForInnerClassDef() {
481         
482          // Test a local inner class definition and reference
483          SymbolData obj = new SymbolData("ILikeMonkey");
484          LanguageLevelConverter.symbolTable.put("ILikeMonkey", obj);
485          InnerClassDef cd0 = 
486            new InnerClassDef(SourceInfo.NONE, 
487                              _packageMav, 
488                              new Word(SourceInfo.NONE, "Rod"),
489                              new TypeParameter[0], 
490                              new ClassOrInterfaceType(SourceInfo.NONE, "ILikeMonkey", new Type[0]), 
491                              new ReferenceType[0], 
492                              new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
493          cd0.visit(_bbv);
494          assertEquals("There should be no errors", 0, errors.size());
495          SymbolData innerClass1 = _bbv._bodyData.getInnerClassOrInterface("Rod");
496          assertNotNull("Should have a inner class named Rod", innerClass1);
497          
498          // Test one with explicit modifiers
499          InnerClassDef cd1 = 
500            new InnerClassDef(SourceInfo.NONE, 
501                              _publicMav, 
502                              new Word(SourceInfo.NONE, "Todd"),
503                              new TypeParameter[0], 
504                              new ClassOrInterfaceType(SourceInfo.NONE, "ILikeMonkey", new Type[0]), 
505                              new ReferenceType[0], 
506                              new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
507          cd1.visit(_bbv);
508          assertEquals("There should be no errors", 0, errors.size());  // modifiers are allowed
509          SymbolData innerClass2 = _bbv._bodyData.getInnerClassOrInterface("Todd");
510          assertNotNull("Should have a inner class named Todd", innerClass2);
511         }
512         
513         public void testForInnerInterfaceDef() {       
514           //Test a trivial inner interface definition
515           InnerInterfaceDef iid = 
516             new InnerInterfaceDef(SourceInfo.NONE, 
517                                   _packageMav, 
518                                   new Word(SourceInfo.NONE, "Broken"),
519                                   new TypeParameter[0], 
520                                   new ReferenceType[0], 
521                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
522           iid.visit(_bbv);
523           assertEquals("There should be one error", 1, errors.size());
524           assertEquals("The error message should be correct", 
525                        "Local interfaces are illegal in Java.", errors.get(0).getFirst());
526           SymbolData innerInterface = _bbv._bodyData.getInnerClassOrInterface("Broken");
527           assertNull("Should NOT have a inner interface named Broken", innerInterface);
528           
529           // Test a inner interface definition and reference
530           InnerInterfaceDef id0 = 
531             new InnerInterfaceDef(SourceInfo.NONE, 
532                                   _packageMav, 
533                                   new Word(SourceInfo.NONE, "RodInterface"),
534                                   new TypeParameter[0], 
535                                   new ReferenceType[0], 
536                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
537           id0.visit(_bbv);
538           assertEquals("There should be 2 errors", 2, errors.size());
539           assertEquals("The error message should be correct", 
540                        "Local interfaces are illegal in Java.", errors.get(1).getFirst());
541           innerInterface = _bbv._bodyData.getInnerClassOrInterface("RodInterface");
542           assertNull("Should NOT have a inner interface named RodInterface", innerInterface);
543           
544           // Test one with explicit modifiers
545           InnerInterfaceDef id1 = 
546             new InnerInterfaceDef(SourceInfo.NONE, 
547                                   _publicMav, 
548                                   new Word(SourceInfo.NONE, "Todd"),
549                                   new TypeParameter[0], 
550                                   new ReferenceType[0], 
551                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
552           id1.visit(_bbv);
553           assertEquals("There should be three errors", 3, errors.size());  // class modifiers are allowed
554           assertEquals("The error message should be correct", 
555                        "Local interfaces are illegal in Java.", errors.get(2).getFirst());
556           innerInterface = _bbv._bodyData.getInnerClassOrInterface("Todd");
557           assertNull("Should NOT have a inner interface named Todd", innerInterface);
558         }
559         
560         public void testDummy() { }
561      }
562    }