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.plt.reflect.JavaVersion;
042    
043    import java.util.*;
044    import java.io.*;
045    
046    import junit.framework.TestCase;
047    
048    
049    
050    /** Language Level Visitor that represents the FullJava Language Level. Only builds the symbol table).  No syntax
051      * checking is performed.  All .java files will be compiled by "javac", which will check for syntax errors.
052      */
053    public class BodyBodyFullJavaVisitor extends FullJavaVisitor {
054    
055      /** The MethodData of this method.*/
056      private BodyData _bodyData;
057      
058      /** Preferred constructor for BodyBodyFullJavaVisitor.
059        * @param bodyData  The BodyData that encloses the context we are visiting.
060        * @param file  The source file this came from.
061        * @param packageName  The package the source file is in
062        * @importedFiles  A list of classes that were specifically imported
063        * @param importedPackages  A list of package names that were specifically imported
064        * @param classesInThisFile  A list of the classes that are yet to be defined in this source file
065        * @param continuations  A hashtable corresponding to the continuations (unresolved Symbol Datas) that will need to 
066        *                       be resolved
067        * @param fixUps  A list of commands to be performed after this pass to fixup the symbolTable
068        * @param innerClassesInThisBody  A list of the names of the inner classes in the enclosing class
069        * @param genericTypes  A table mapping the generic type names that are in scope to their bounds
070        */
071      public BodyBodyFullJavaVisitor(BodyData bodyData,
072                                     File file, 
073                                     String packageName,
074                                     String enclosingClassName,
075                                     LinkedList<String> importedFiles, 
076                                     LinkedList<String> importedPackages, 
077                                     HashSet<String> classesInThisFile, 
078                                     Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations,
079                                     LinkedList<Command> fixUps,
080                                     HashSet<String> innerClassesInThisBody,
081                                     HashMap<String, SymbolData> genericTypes) { 
082        super(file, packageName, enclosingClassName, importedFiles, importedPackages, classesInThisFile, continuations, 
083              fixUps, genericTypes);
084        
085        _bodyData = bodyData;
086        
087        SymbolData objectSD = symbolTable.get("java.lang.Object");   
088        SymbolData integerSD = symbolTable.get("java.lang.Integer");
089        assert objectSD != null && integerSD != null;
090        assert integerSD.isAssignableTo(objectSD, JavaVersion.JAVA_5);
091      }
092       
093      
094      /** Legacy Constructor for BodyBodyFullJavaVisitor.
095        * @param bodyData  The BodyData that encloses the context we are visiting.
096        * @param file  The source file this came from.
097        * @param packageName  The package the source file is in
098        * @importedFiles  A list of classes that were specifically imported
099        * @param importedPackages  A list of package names that were specifically imported
100        * @param classesInThisFile  A list of the classes that are yet to be defined in this source file
101        * @param continuations  A hashtable corresponding to the continuations (unresolved Symbol Datas) that will need to 
102        *                       be resolved
103        * @param fixUps  A list of commands to be performed after this pass to fixup the symbolTable
104        */
105      public BodyBodyFullJavaVisitor(BodyData bodyData,
106                                     File file, 
107                                     String packageName,
108                                     String enclosingClassName,
109                                     LinkedList<String> importedFiles, 
110                                     LinkedList<String> importedPackages, 
111                                     HashSet<String> classesInThisFile, 
112                                     Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations,
113                                     LinkedList<Command> fixUps,
114                                     HashSet<String> innerClassesInThisBody) {
115        super(file, packageName, enclosingClassName, importedFiles, importedPackages, classesInThisFile, continuations, fixUps);
116        _bodyData = bodyData;
117        
118        SymbolData objectSD = symbolTable.get("java.lang.Object");   
119        SymbolData integerSD = symbolTable.get("java.lang.Integer");
120        assert objectSD != null && integerSD != null;
121        assert integerSD.isAssignableTo(objectSD, JavaVersion.JAVA_5);
122      }
123      
124      /** Ignore MethodDef. */
125      public Void forMethodDefDoFirst(MethodDef that) { return null; }
126      
127      /* There is currently no way to differentiate between a block statement and
128       * an instance initializer in a braced body given the general nature of a 
129       * braced body.  Whenever an instance initialization is visited in a method
130       * body, we must assume that it is a block statement.
131       */
132      public Void forInstanceInitializer(InstanceInitializer that) { return forBlock(that.getCode());  }
133    
134      /* Visit this BlockData with a new BodyBodyFullJava visitor. */
135      public Void forBlock(Block that) {
136        forBlockDoFirst(that);
137        if (prune(that)) return null;
138        BlockData bd = new BlockData(_bodyData);
139        _bodyData.addBlock(bd);
140        that.getStatements().visit(new BodyBodyFullJavaVisitor(bd, _file, _package, _enclosingClassName, _importedFiles, 
141                                                               _importedPackages, _classesInThisFile, continuations, fixUps,
142                                                               new HashSet<String>(), _genericTypes));
143        return forBlockOnly(that);
144      }
145      
146      /** Visit the block as in forBlock(), but first add the exception parameter as a variable in that block. */
147      public Void forCatchBlock(CatchBlock that) {
148        forCatchBlockDoFirst(that);
149        if (prune(that)) return null;
150        
151        Block b = that.getBlock();
152        forBlockDoFirst(b);
153        if (prune(b)) return null;
154        BlockData bd = new BlockData(_bodyData);
155        _bodyData.addBlock(bd);
156        
157        SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
158        VariableData exceptionVar = 
159          formalParameters2VariableData(new FormalParameter[]{ that.getException() }, enclosing)[0];  // !!!!! Why not bd?
160        bd.addVar(exceptionVar);
161        
162        BodyBodyFullJavaVisitor bbfjv = 
163          new BodyBodyFullJavaVisitor(bd, _file, _package, _enclosingClassName, _importedFiles,
164                                      _importedPackages, _classesInThisFile, continuations, fixUps,
165                                      new HashSet<String>(), _genericTypes);
166        b.getStatements().visit(bbfjv);
167        forBlockOnly(b);
168        return forCatchBlockOnly(that);
169      }
170      
171      /** Adds the variables that were declared to the body data and make sure that no two variables have the same name.*/
172      public Void forVariableDeclarationOnly(VariableDeclaration that) {
173    //    System.err.println("Calling _variableDeclaration2VariableData in BodyBodyFullJavaVisitor.java");
174        if (! _bodyData.addVars(_variableDeclaration2VariableData(that, _bodyData))) {
175    /* The following commenting out of code is kludge to get around the fact that LL processing does not allow a for
176     * index variable to repeated in successive for loops. TODO: fix this. */
177    //      _addAndIgnoreError("You cannot have two variables with the same name.", that);
178        }
179        return null;
180      }
181      
182      /** Ignore TryCatchStatement.*/
183      public Void forTryCatchStatementDoFirst(TryCatchStatement that) { return null; }
184    
185      /** Process a local class definition */
186      public Void forInnerClassDef(InnerClassDef that) {
187        // TODO: is this necessarily local?
188        SymbolData enclosingClass = _bodyData.getSymbolData();
189        assert _enclosingClassName.equals(getQualifiedClassName(enclosingClass.getName()));
190        
191        String relName = that.getName().getText();
192        String fullName = _enclosingClassName + '$' + enclosingClass.preincrementLocalClassNum() + relName;
193    //    System.err.println("***ALARM*** Processing local class '" + relName + "' in class " + _enclosingClassName
194    //                         + " with flattened class name " + fullName);
195        handleInnerClassDef(that, _bodyData, relName, fullName);
196        // How do we know that generated number is correct?
197        return null;
198      }
199      
200      /** Process a local interface definition */
201      public Void forInnerInterfaceDef(InnerInterfaceDef that) {
202        _addAndIgnoreError("Local interfaces are illegal in Java.", that);
203        return null;
204      }
205      
206    //  /** Delegate to method in LLV. */
207    //  public Void forSimpleAnonymousClassInstantiation(SimpleAnonymousClassInstantiation that) {
208    //    simpleAnonymousClassInstantiationHelper(that, _bodyData);
209    //    return null;
210    //  }
211    //  
212    //    /** Delegate to helper method. */
213    //  public Void forComplexAnonymousClassInstantiation(ComplexAnonymousClassInstantiation that) {
214    //    SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
215    //    assert enclosing != null;
216    //    complexAnonymousClassInstantiationHelper(that, enclosing);  // TODO: the wrong enclosing context?
217    //    return null;
218    //  }
219    
220      /** Delegate to helper method. */
221      public Void forSimpleAnonymousClassInstantiation(SimpleAnonymousClassInstantiation that) {
222        SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
223        assert enclosing != null;
224        simpleAnonymousClassInstantiationHelper(that, enclosing);
225        return null;
226      }
227      
228      /** Delegate to helper method. */
229      public Void forComplexAnonymousClassInstantiation(ComplexAnonymousClassInstantiation that) {
230        SymbolData enclosing = getQualifiedSymbolData(_enclosingClassName);
231        assert enclosing != null;
232        complexAnonymousClassInstantiationHelper(that, enclosing);  // TODO: the wrong enclosing context?
233        return null;
234      }
235    
236      /** Test most of the methods declared above right here. */
237      public static class BodyBodyFullJavaVisitorTest extends TestCase {
238        
239        private BodyBodyFullJavaVisitor _bfv;
240        
241        private SymbolData _sd1;
242        private MethodData _md1;
243        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
244        private ModifiersAndVisibility _protectedMav = 
245          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
246        private ModifiersAndVisibility _privateMav = 
247          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
248        private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
249        private ModifiersAndVisibility _abstractMav = 
250          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
251        private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
252        private ModifiersAndVisibility _staticMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"static"});
253        
254        
255        public BodyBodyFullJavaVisitorTest() { this(""); }
256        
257        public BodyBodyFullJavaVisitorTest(String name) { super(name); }
258        
259        public void setUp() {
260          _sd1 = new SymbolData("i.like.monkey");
261          _md1 = new MethodData("methodName", _publicMav, new TypeParameter[0], SymbolData.INT_TYPE, 
262                                       new VariableData[0], 
263                                       new String[0],
264                                       _sd1,
265                                       null);
266    
267          errors = new LinkedList<Pair<String, JExpressionIF>>();
268          LanguageLevelConverter.symbolTable.clear();
269          LanguageLevelConverter._newSDs.clear();
270          // Use _sd1 for _enclosingClassName
271          LanguageLevelConverter.symbolTable.put("i.like.monkey", _sd1);
272          visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();      
273    //      _hierarchy = new Hashtable<String, TypeDefBase>();
274          _bfv = new BodyBodyFullJavaVisitor(_md1, 
275                                             new File(""), 
276                                             "",
277                                             "i.like.monkey",
278                                             new LinkedList<String>(), 
279                                             new LinkedList<String>(), 
280                                             new HashSet<String>(), 
281                                             new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
282                                             new LinkedList<Command>(),
283                                             new HashSet<String>());
284          assert _bfv._enclosingClassName.equals("i.like.monkey");
285          _bfv._classesInThisFile = new HashSet<String>();
286          _bfv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
287    //      _bfv._resetNonStaticFields();  // clobbers _package and _enclosingClassName
288          _bfv._importedPackages.addFirst("java.lang");
289          _errorAdded = false;
290          _sd1.setIsContinuation(false);
291          _sd1.setInterface(false);
292          _sd1.setPackage("");
293          _sd1.setTypeParameters(new TypeParameter[0]);
294          SymbolData objectSD = _bfv.getQualifiedSymbolData("java.lang.Object", SourceInfo.NONE);
295          _sd1.setSuperClass(objectSD);
296          _sd1.setInterfaces(new ArrayList<SymbolData>());
297        }
298        
299        public void testSetUp() {
300          assertFalse("i.like.monkey is present", _bfv.getQualifiedSymbolData("i.like.monkey", SourceInfo.NONE) == null);
301          assertEquals("_enclosingClassName is set", "i.like.monkey", _bfv._enclosingClassName);
302        }
303        
304        public void testForMethodDefDoFirst() {
305          ConcreteMethodDef cmd = new ConcreteMethodDef(SourceInfo.NONE, 
306                                                        _packageMav, 
307                                                        new TypeParameter[0], 
308                                                        new PrimitiveType(SourceInfo.NONE, "int"), 
309                                                        new Word(SourceInfo.NONE, "methodName"),
310                                                        new FormalParameter[0],
311                                                        new ReferenceType[0], 
312                                                        new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
313          cmd.visit(_bfv);
314          assertEquals("There should be no errors", 0, errors.size());  // This can happen in a local inner class
315    //      assertEquals("The error message should be correct.", 
316    //                   "Methods definitions cannot appear within the body of another method or block.",
317    //                   errors.get(0).getFirst());
318        }
319        
320        /* These last two tests are shared with ClassBodyIntermediateVisitor,
321         * perhaps we could factor them out. */
322        
323        public void testForVariableDeclarationOnly() {
324          // Check one that works
325          VariableDeclarator[] vdecs = new VariableDeclarator[] {
326            new UninitializedVariableDeclarator(SourceInfo.NONE, 
327                                                new PrimitiveType(SourceInfo.NONE, "double"), 
328                                                new Word (SourceInfo.NONE, "field1")),
329              new UninitializedVariableDeclarator(SourceInfo.NONE, 
330                                                  new PrimitiveType(SourceInfo.NONE, "boolean"), 
331                                                  new Word (SourceInfo.NONE, "field2"))};
332          VariableDeclaration vdecl = new VariableDeclaration(SourceInfo.NONE, _packageMav, vdecs);
333          
334          VariableData vd1 = new VariableData("field1", _packageMav, SymbolData.DOUBLE_TYPE, false, _bfv._bodyData);
335          VariableData vd2 = new VariableData("field2", _packageMav, SymbolData.BOOLEAN_TYPE, false, _bfv._bodyData);
336          vdecl.visit(_bfv);
337          assertEquals("There should not be any errors.", 0, errors.size());
338          LinkedList<VariableData> vars = _md1.getVars();
339    //      for (int i = 0; i < vars.size(); i++) {
340    //        System.err.println(vars.get(i).getName() + " " + vars.get(i).getMav() + " " + vars.get(i).getType().getName() 
341    //                            + " " + vars.get(i).hasValue() + " " + vars.get(i).getEnclosingData().getName());
342    //      }
343    //      System.err.println("vars[0] = " + vars.get(0));
344    //      System.err.println("vd1 = " + vd1);
345          assertTrue("field1 was added.", vars.contains(vd1));
346          assertTrue("field2 was added.", vars.contains(vd2));
347          
348          // Check one that doesn't work
349          VariableDeclaration vdecl2 = new VariableDeclaration(SourceInfo.NONE,
350                                                            _packageMav,
351                                                            new VariableDeclarator[] {
352            new UninitializedVariableDeclarator(SourceInfo.NONE, 
353                                                new PrimitiveType(SourceInfo.NONE, "double"), 
354                                                new Word (SourceInfo.NONE, "field3")),
355            new UninitializedVariableDeclarator(SourceInfo.NONE, 
356                                                new PrimitiveType(SourceInfo.NONE, "int"), 
357                                                new Word (SourceInfo.NONE, "field3"))});
358          VariableData vd3 = new VariableData("field3", _packageMav, SymbolData.DOUBLE_TYPE, false, _bfv._bodyData);
359          vdecl2.visit(_bfv);
360          assertEquals("There should still be no errors.", 0, errors.size());
361          
362    /* The following test was commented out because of the kludge introduced in forVariableDeclarationOnly above */      
363    //      assertEquals("There should be one error.", 1, errors.size());
364    //      assertEquals("The error message should be correct", 
365    //                   "You cannot have two variables with the same name.", errors.get(0).getFirst());
366          assertTrue("field3 was added.", _md1.getVars().contains(vd3));
367        }
368        
369        public void testForTryCatchStatement() {
370          //Make sure that no error is thrown
371          BracedBody emptyBody = new BracedBody(SourceInfo.NONE, new BodyItemI[0]);
372          Block b = new Block(SourceInfo.NONE, emptyBody);
373    
374          NormalTryCatchStatement ntcs = new NormalTryCatchStatement(SourceInfo.NONE, b, new CatchBlock[0]);
375          TryCatchFinallyStatement tcfs = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[0], b);
376          ntcs.visit(_bfv);
377          tcfs.visit(_bfv);
378          assertEquals("After visiting NormalTryCatchStatement and TryCatchFinallyStatement, there should be no errors", 
379                       0, errors.size());
380          
381          //make sure that if there is an error in one of the bodies, it is caught:   (this is an arbitrary error).
382          BracedBody errorBody = new BracedBody(SourceInfo.NONE, new BodyItemI[] {
383            new ExpressionStatement(SourceInfo.NONE, 
384                                    new BitwiseOrExpression(SourceInfo.NONE, 
385                                                            new IntegerLiteral(SourceInfo.NONE, 1), 
386                                                            new IntegerLiteral(SourceInfo.NONE, 2)))});
387          Block errorBlock = new Block(SourceInfo.NONE, errorBody);
388          
389          ntcs = new NormalTryCatchStatement(SourceInfo.NONE, errorBlock, new CatchBlock[0]);
390          ntcs.visit(_bfv);
391    //      if (errors.size() > 0) System.err.println("Error was:" + errors.get(0).getFirst());
392          assertEquals("Should be no errors", 0, errors.size());  // bitwise operations are allowed
393          
394          // make sure that if there is an error in one of the catch statements, it is caught: (this is an arbitrary error).
395          UninitializedVariableDeclarator uvd = 
396            new UninitializedVariableDeclarator(SourceInfo.NONE, 
397                                                new PrimitiveType(SourceInfo.NONE, "int"), 
398                                                new Word(SourceInfo.NONE, "i"));
399          FormalParameter fp = new FormalParameter(SourceInfo.NONE, uvd, false);
400    
401          tcfs = new TryCatchFinallyStatement(SourceInfo.NONE, b, new CatchBlock[] {
402            new CatchBlock(SourceInfo.NONE, fp, errorBlock)}, b);
403            
404         tcfs.visit(_bfv);
405    //     if (errors.size() > 0) System.err.println("Error was:" + errors.get(0).getFirst());
406         assertEquals("Should be no errors", 0, errors.size());  // bitwise operations are allowed
407        }
408        
409         public void testForInnerClassDef() {
410         
411          // Test a local inner class definition and reference
412          InnerClassDef cd0 = 
413            new InnerClassDef(SourceInfo.NONE, 
414                              _packageMav, 
415                              new Word(SourceInfo.NONE, "Rod"),
416                              new TypeParameter[0], 
417                              new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]), 
418                              new ReferenceType[0], 
419                              new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
420          cd0.visit(_bfv);
421          assertEquals("There should be no errors", 0, errors.size());
422          SymbolData innerClass = _bfv._bodyData.getInnerClassOrInterface("Rod");
423          assertNotNull("Should have a inner class named Rod", innerClass);
424               
425          // Test one with explicit modifiers
426          InnerClassDef cd1 = 
427            new InnerClassDef(SourceInfo.NONE, 
428                              _publicMav, 
429                              new Word(SourceInfo.NONE, "Todd"),
430                              new TypeParameter[0], 
431                              new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.Object", new Type[0]), 
432                              new ReferenceType[0], 
433                              new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
434          cd1.visit(_bfv);
435          assertEquals("There should be no errors", 0, errors.size());  // class modifiers are allowed
436        }
437        
438         public void testForInnerInterfaceDef() {       
439           //Test a trivial inner interface definition
440           InnerInterfaceDef iid = 
441             new InnerInterfaceDef(SourceInfo.NONE, 
442                                   _packageMav, 
443                                   new Word(SourceInfo.NONE, "Broken"),
444                                   new TypeParameter[0], 
445                                   new ReferenceType[0], 
446                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
447           iid.visit(_bfv);
448           assertEquals("There should be one error", 1, errors.size());
449           assertEquals("The error message should be correct", 
450                       "Local interfaces are illegal in Java.", errors.get(0).getFirst());
451           SymbolData innerInterface = _bfv._bodyData.getInnerClassOrInterface("Broken");
452           assertNull("Should NOT have a inner interface named Broken", innerInterface);
453           
454           // Test a inner interface definition and reference
455           InnerInterfaceDef id0 = 
456             new InnerInterfaceDef(SourceInfo.NONE, 
457                                   _packageMav, 
458                                   new Word(SourceInfo.NONE, "RodInterface"),
459                                   new TypeParameter[0], 
460                                   new ReferenceType[0], 
461                                   new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
462           id0.visit(_bfv);
463           assertEquals("There should be 2 errors", 2, errors.size());
464           assertEquals("The error message should be correct", 
465                        "Local interfaces are illegal in Java.", errors.get(1).getFirst());
466           innerInterface = _bfv._bodyData.getInnerClassOrInterface("RodInterface");
467           assertNull("Should NOT have a inner interface named RodInterface", innerInterface);
468           
469           // Test one with explicit modifiers
470          InnerInterfaceDef id1 = 
471            new InnerInterfaceDef(SourceInfo.NONE, 
472                              _publicMav, 
473                              new Word(SourceInfo.NONE, "Todd"),
474                              new TypeParameter[0], 
475                              new ReferenceType[0], 
476                              new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
477          id1.visit(_bfv);
478          assertEquals("There should be three errors", 3, errors.size());  // class modifiers are allowed
479          assertEquals("The error message should be correct", 
480                       "Local interfaces are illegal in Java.", errors.get(2).getFirst());
481          innerInterface = _bfv._bodyData.getInnerClassOrInterface("Todd");
482          assertNull("Should NOT have a inner interface named Todd", innerInterface);
483         }
484         public void testDummy() { }
485      }
486    }