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 import edu.rice.cs.plt.iter.IterUtil;
043 import java.util.*;
044 import java.io.*;
045
046 import junit.framework.TestCase;
047
048 /** Language Level Visitor that represents the Intermediate Language Level. Enforces constraints during the first walk of
049 * the AST (checking for langauge specific errors and building the symbol table). This class enforces things that are
050 * common to all contexts reachable within an interface body at the Intermediate Language Level.
051 */
052 public class InterfaceBodyIntermediateVisitor extends IntermediateVisitor {
053
054 /**The SymbolData corresponding to this interface.*/
055 private SymbolData _enclosing;
056
057 /** Constructor for InterfaceBodyIntermediateVisitor.
058 * @param sd The SymbolData 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 to be resolved
065 */
066 public InterfaceBodyIntermediateVisitor(SymbolData sd,
067 File file,
068 String packageName,
069 LinkedList<String> importedFiles,
070 LinkedList<String> importedPackages,
071 HashSet<String> classesInThisFile,
072 Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations,
073 LinkedList<Command> fixUps) {
074 super(file, packageName, sd.getName(), importedFiles, importedPackages, classesInThisFile, continuations, fixUps);
075 _enclosing = sd;
076 }
077
078 /*Throw an appropriate error*/
079 public Void forStatementDoFirst(Statement that) {
080 _addError("Statements cannot appear outside of method bodies", that);
081 return null;
082 }
083
084 /*Throw an appropriate error*/
085 public Void forConcreteMethodDefDoFirst(ConcreteMethodDef that) {
086 _addError("You cannot have concrete methods definitions in interfaces", that);
087 return null;
088 }
089
090 /**Throw an appropriate error*/
091 public Void forInstanceInitializerDoFirst(InstanceInitializer that) {
092 _addError("This open brace must mark the beginning of an interface body", that);
093 return null;
094 }
095
096 /**No fields in interfaces at Intermediate Level. Give an appropriate error.*/
097 public Void forVariableDeclarationDoFirst(VariableDeclaration that) {
098 _addError("You cannot have fields in interfaces at the Intermediate level", that);
099 return null;
100 }
101
102 /** No super references for interfaces--give an appropriate error.*/
103 public Void forSuperReferenceDoFirst(SuperReference that) {
104 _addAndIgnoreError("The field 'super' does not exist in interfaces. Only classes have a 'super' field", that);
105 return null;
106 }
107
108 /**No This literal in interfaces--give an appropriate error*/
109 public Void forThisReferenceDoFirst(ThisReference that) {
110 _addAndIgnoreError("The field 'this' does not exist in interfaces. Only classes have a 'this' field.", that);
111 return null;
112 }
113
114 /* Make sure that the method is not declared to be private or protected. Make it public and abstract
115 * if it is not already declared to be so (since this is the default in the absence of modifiers).
116 * Make sure the method name is not the same as the interface name.
117 */
118 public Void forAbstractMethodDef(AbstractMethodDef that) {
119 forAbstractMethodDefDoFirst(that);
120 if (_checkError()) return null;
121
122 MethodData md = createMethodData(that, _enclosing);
123
124 //All interface methods are considered public by default: enforce this.
125 if (md.hasModifier("private")) {
126 _addAndIgnoreError("Interface methods cannot be private. They must be public.", that.getMav());
127 }
128 if (md.hasModifier("protected")) {
129 _addAndIgnoreError("Interface methods cannot be protected. They must be public.", that.getMav());
130 }
131
132 // All interface methods are considered public by default.
133 md.addModifier("public");
134 md.addModifier("abstract"); //and all interface methods are abstract.
135 String className = getUnqualifiedClassName(_enclosing.getName());
136 if (className.equals(md.getName())) {
137 _addAndIgnoreError("Only constructors can have the same name as the class they appear in, " +
138 "and constructors cannot appear in interfaces.", that);
139 }
140 else _enclosing.addMethod(md);
141 return null;
142 }
143
144 /** Throw an error: Interfaces cannot have constructors */
145 public Void forConstructorDefDoFirst(ConstructorDef that) {
146 _addAndIgnoreError("Constructor definitions cannot appear in interfaces", that);
147 return null;
148 }
149
150 /** Processes a static field declaration within an interface. Calls the super method to convert the VariableDeclaration
151 * to a VariableData array, then makes sure that each VariableData is final and static, as required in an
152 * interface.
153 * @param enclosingData The Data immediately enclosing the variables
154 */
155 protected VariableData[] _variableDeclaration2VariableData(VariableDeclaration vd, Data enclosingData) {
156 VariableData[] vds = super._variableDeclaration2VariableData(vd, enclosingData);
157 for (int i = 0; i < vds.length; i++) {
158 vds[i].setFinalAndStatic();
159 }
160 return vds;
161 }
162
163 /** Delegate to method in LLV */
164 public Void forComplexAnonymousClassInstantiation(ComplexAnonymousClassInstantiation that) {
165 complexAnonymousClassInstantiationHelper(that, _enclosing); // TODO: the wrong enclosing context?
166 return null;
167 }
168
169 /** Delegate to method in LLV */
170 public Void forSimpleAnonymousClassInstantiation(SimpleAnonymousClassInstantiation that) {
171 simpleAnonymousClassInstantiationHelper(that, _enclosing);
172 return null;
173 }
174
175 /** Test the methods declared in the above class. */
176 public static class InterfaceBodyIntermediateVisitorTest extends TestCase {
177
178 private InterfaceBodyIntermediateVisitor _ibiv;
179
180 private SymbolData _sd1;
181 private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
182 private ModifiersAndVisibility _protectedMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
183 private ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
184 private ModifiersAndVisibility _packageMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
185 private ModifiersAndVisibility _abstractMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
186 private ModifiersAndVisibility _finalMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
187
188
189 public InterfaceBodyIntermediateVisitorTest() {
190 this("");
191 }
192 public InterfaceBodyIntermediateVisitorTest(String name) {
193 super(name);
194 }
195
196 public void setUp() {
197 _sd1 = new SymbolData("i.like.monkey");
198 _sd1.setIsContinuation(false);
199 _sd1.setInterface(false);
200 _sd1.setPackage("");
201 _sd1.setTypeParameters(new TypeParameter[0]);
202 _sd1.setInterfaces(new ArrayList<SymbolData>());
203
204 errors = new LinkedList<Pair<String, JExpressionIF>>();
205 LanguageLevelConverter.symbolTable.clear();
206 LanguageLevelConverter._newSDs.clear();
207 LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, IterUtil.make(new File("lib/buildlib/junit.jar")));
208 visitedFiles = new LinkedList<Pair<LanguageLevelVisitor, edu.rice.cs.javalanglevels.tree.SourceFile>>();
209 // _hierarchy = new Hashtable<String, TypeDefBase>();
210 _ibiv =
211 new InterfaceBodyIntermediateVisitor(_sd1,
212 new File(""),
213 "",
214 new LinkedList<String>(),
215 new LinkedList<String>(),
216 new HashSet<String>(),
217 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
218 new LinkedList<Command>());
219 _ibiv._classesInThisFile = new HashSet<String>();
220 _ibiv.continuations = new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
221 _ibiv._importedPackages.addFirst("java.lang");
222 _ibiv._enclosingClassName = "i.like.monkey";
223 _ibiv.symbolTable.put("i.like.monkey", _sd1);
224 _errorAdded = false;
225 }
226
227 public void testForConcreteMethodDefDoFirst() {
228 // Check that an error is thrown
229 ConcreteMethodDef cmd = new ConcreteMethodDef(SourceInfo.NONE,
230 _publicMav,
231 new TypeParameter[0],
232 new PrimitiveType(SourceInfo.NONE, "int"),
233 new Word(SourceInfo.NONE, "methodName"),
234 new FormalParameter[0],
235 new ReferenceType[0],
236 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
237 cmd.visit(_ibiv);
238 assertEquals("There should not be 1 error", 1, errors.size());
239 assertEquals("The error message should be correct", "You cannot have concrete methods definitions in interfaces", errors.getLast().getFirst());
240
241 }
242
243 public void testForAbstractMethodDefDoFirst() {
244 // Check one that works
245 _ibiv._enclosing.setMav(_abstractMav);
246 AbstractMethodDef amd2 = new AbstractMethodDef(SourceInfo.NONE,
247 _abstractMav,
248 new TypeParameter[0],
249 new PrimitiveType(SourceInfo.NONE, "double"),
250 new Word(SourceInfo.NONE, "methodName"),
251 new FormalParameter[0],
252 new ReferenceType[0]);
253 amd2.visit(_ibiv);
254 assertEquals("There should be no errors", 0, errors.size());
255 assertTrue("The method def should be public", _ibiv._enclosing.getMethods().get(0).hasModifier("public"));
256
257 }
258
259 public void testForInstanceInitializerDoFirst() {
260 InstanceInitializer ii = new InstanceInitializer(SourceInfo.NONE,
261 new Block(SourceInfo.NONE,
262 new BracedBody(SourceInfo.NONE, new BodyItemI[0])));
263 ii.visit(_ibiv);
264 assertEquals("There should be one error.", 1, errors.size());
265 assertEquals("The error message should be correct.", "This open brace must mark the beginning of an interface body", errors.get(0).getFirst());
266 }
267
268 public void testForSimpleThisReferenceDoFirst() {
269 SimpleThisReference tl = new SimpleThisReference(SourceInfo.NONE);
270 tl.visit(_ibiv);
271 assertEquals("There should be one error", 1, errors.size());
272 assertEquals("The error message should be correct", "The field 'this' does not exist in interfaces. Only classes have a 'this' field.", errors.get(0).getFirst());
273 }
274
275
276 public void testForComplexThisReferenceDoFirst() {
277 ComplexThisReference tl = new ComplexThisReference(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE));
278 tl.visit(_ibiv);
279 assertEquals("There should be one error", 1, errors.size());
280 assertEquals("The error message should be correct", "The field 'this' does not exist in interfaces. Only classes have a 'this' field.", errors.get(0).getFirst());
281
282 }
283
284 public void testForSimpleSuperReferenceDoFirst() {
285 SimpleSuperReference sr = new SimpleSuperReference(SourceInfo.NONE);
286 sr.visit(_ibiv);
287 assertEquals("There should be one error", 1, errors.size());
288 assertEquals("The error message should be correct", "The field 'super' does not exist in interfaces. Only classes have a 'super' field", errors.get(0).getFirst());
289 }
290
291 public void testForComplexSuperReferenceDoFirst() {
292 ComplexSuperReference cr = new ComplexSuperReference(SourceInfo.NONE, new NullLiteral(SourceInfo.NONE));
293 cr.visit(_ibiv);
294 assertEquals("There should be one error", 1, errors.size());
295 assertEquals("The error message should be correct", "The field 'super' does not exist in interfaces. Only classes have a 'super' field", errors.get(0).getFirst());
296 }
297
298
299 public void testForVariableDeclarationDoFirst() {
300 // Check that an error is thrown
301 VariableDeclaration vdecl = new VariableDeclaration(SourceInfo.NONE,
302 _packageMav,
303 new VariableDeclarator[] {
304 new UninitializedVariableDeclarator(SourceInfo.NONE,
305 new PrimitiveType(SourceInfo.NONE, "double"),
306 new Word (SourceInfo.NONE, "field1")),
307 new UninitializedVariableDeclarator(SourceInfo.NONE,
308 new PrimitiveType(SourceInfo.NONE, "boolean"),
309 new Word (SourceInfo.NONE, "field2"))});
310 vdecl.visit(_ibiv);
311 assertEquals("There should be one error", 1, errors.size());
312 assertEquals("The error message should be correct", "You cannot have fields in interfaces at the Intermediate level", errors.getLast().getFirst());
313 }
314
315 public void testForAbstractMethodDef() {
316 // Test one that works.
317 MethodDef mdef = new AbstractMethodDef(SourceInfo.NONE,
318 _abstractMav,
319 new TypeParameter[0],
320 new PrimitiveType(SourceInfo.NONE, "int"),
321 new Word(SourceInfo.NONE, "methodName"),
322 new FormalParameter[0],
323 new ReferenceType[0]);
324 _ibiv._enclosing.setMav(_abstractMav);
325
326 mdef.visit(_ibiv);
327 assertEquals("There should not be any errors.", 0, errors.size());
328
329 // Test one that doesn't work.
330 mdef = new AbstractMethodDef(SourceInfo.NONE,
331 _abstractMav,
332 new TypeParameter[0],
333 new PrimitiveType(SourceInfo.NONE, "int"),
334 new Word(SourceInfo.NONE, "monkey"),
335 new FormalParameter[0],
336 new ReferenceType[0]);
337 mdef.visit(_ibiv);
338 assertEquals("There should be one error.", 1, errors.size());
339 assertEquals("The error message should be correct.",
340 "Only constructors can have the same name as the class they appear in, and constructors cannot appear in interfaces.",
341 errors.get(0).getFirst());
342
343
344 //It's okay for the method to be public
345 AbstractMethodDef amd3 = new AbstractMethodDef(SourceInfo.NONE,
346 _publicMav,
347 new TypeParameter[0],
348 new PrimitiveType(SourceInfo.NONE, "double"),
349 new Word(SourceInfo.NONE, "methodName2"),
350 new FormalParameter[0],
351 new ReferenceType[0]);
352 amd3.visit(_ibiv);
353 assertEquals("There should still be one error", 1, errors.size());
354 assertTrue("The method def should be public", _ibiv._enclosing.getMethods().get(1).hasModifier("public"));
355
356 //What if the method is called private? Should throw error
357 AbstractMethodDef amd4 = new AbstractMethodDef(SourceInfo.NONE,
358 _privateMav,
359 new TypeParameter[0],
360 new PrimitiveType(SourceInfo.NONE, "double"),
361 new Word(SourceInfo.NONE, "methodName3"),
362 new FormalParameter[0],
363 new ReferenceType[0]);
364 amd4.visit(_ibiv);
365 assertEquals("There should be two errors", 2, errors.size());
366 assertEquals("The error message should be correct","Interface methods cannot be private. They must be public." , errors.get(1).getFirst());
367
368 //What if the method is protected: Should throw error
369 AbstractMethodDef amd5 = new AbstractMethodDef(SourceInfo.NONE,
370 _protectedMav,
371 new TypeParameter[0],
372 new PrimitiveType(SourceInfo.NONE, "double"),
373 new Word(SourceInfo.NONE, "methodName4"),
374 new FormalParameter[0],
375 new ReferenceType[0]);
376 amd5.visit(_ibiv);
377 assertEquals("There should be three errors", 3, errors.size());
378 assertEquals("The error message should be correct","Interface methods cannot be protected. They must be public." , errors.get(2).getFirst());
379 }
380
381
382 public void testForConstructorDef() {
383 ///this is a ConstructorDef with no formal paramaters and no throws
384 ConstructorDef cd = new ConstructorDef(SourceInfo.NONE, new Word(SourceInfo.NONE, "MyClass"), _publicMav, new FormalParameter[0], new ReferenceType[0],
385 new BracedBody(SourceInfo.NONE, new BodyItemI[0]));
386
387 //Check that the appropriate error is thrown.
388 cd.visit(_ibiv);
389 assertEquals("There should now be one error", 1, errors.size());
390 assertEquals("The error message should be correct", "Constructor definitions cannot appear in interfaces", errors.get(0).getFirst());
391
392 }
393 }
394 }