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    }