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 }