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 java.util.*;
042    import junit.framework.*;
043    import java.io.File;
044    import edu.rice.cs.plt.reflect.JavaVersion;
045    
046    /**
047     * Represents the data for an array class.  There are two states of SymbolData.  One is a continuation which
048     * is created when a type is referenced, but the corresponding class has not been read for its members.  The
049     * other is a complete SymbolData containing all of the member data.
050     * This ArrayData stores the SymbolData of its element type inside of it.
051     */
052    
053    public class ArrayData extends SymbolData {
054      /**The type of the elements of this array.  For example, int[] has an _elementType of int.*/
055      private SymbolData _elementType;
056      
057      /** Creates a new ArrayData corresponding to the elementType sd.
058        * @param sd   The SymbolData element type; may be a continuation
059        * @param llv  The LanguageLevelVisitor who created this ArrayData.
060        * @param si   The SourceInfo corresponding to this ArrayData.
061        */
062      public ArrayData(SymbolData sd, LanguageLevelVisitor llv, SourceInfo si) {
063        super(sd.getName() + "[]");
064        
065        _elementType = sd;
066        
067        // Arrays only have one field called length, and it is automatically given a value
068        addVar(new VariableData("length", 
069                                new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public", "final"}),
070                                SymbolData.INT_TYPE, true, this)); 
071        
072        // All arrays are a subclass of Object
073        SymbolData object = llv.getSymbolData("java.lang.Object", si);
074        setSuperClass(object);
075        
076        //All arrays implement java.lang.Cloneable and java.io.Serializable
077        SymbolData result = llv.getSymbolData("java.lang.Cloneable", si);
078        if (result != null) { addInterface(result); }
079        
080        result = llv.getSymbolData("java.io.Serializable", si);
081        if (result != null) { addInterface(result); }
082        
083        //And, since they implement Cloneable, all arrays overwrite the clone method so that it does not throw exceptions
084        addMethod(new MethodData("clone", 
085                                 new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"}), 
086                                 new TypeParameter[0],
087                                 object,
088                                 new VariableData[0],
089                                 new String[0], //Doesn't throw any exceptions!
090                                 this,
091                                 null));                        
092        setIsContinuation(false);
093      }
094    
095      /** @return the package of the element type*/
096      public String getPackage() { return _elementType.getPackage(); }
097      
098      /** Set the package of the element type to be the specified package:
099        * @param s  The package to use*/
100      public void setPackage(String s) { _elementType.setPackage(s); }
101      
102      
103      /* @return the ModifiersAndVisibility of the element type*/
104      public ModifiersAndVisibility getMav() {
105        if (_elementType.hasModifier("final"))  return _elementType.getMav();
106        else {
107          String[] elementMavs = _elementType.getMav().getModifiers();
108          String[] newMavs = new String[elementMavs.length + 1];
109          for (int i = 0; i < elementMavs.length; i++) { newMavs[i] = elementMavs[i]; }
110          newMavs[elementMavs.length] = "final";
111          
112          return new ModifiersAndVisibility(SourceInfo.NONE, newMavs);
113        }
114      }
115      
116      /** Sets the ModifiersAndVisibility of the element type to the specified value.
117        * @param mv  The ModifiersAndVisibility to use. */
118      public void setMav(ModifiersAndVisibility mv) { _elementType.setMav(mv); }
119      
120      /** @return the SymbolData element type corresponding to the elements of this array.*/
121      public SymbolData getElementType() { return _elementType; }
122      
123      /** Delegates to the outer data of your element type*/
124      public Data getOuterData() { return _elementType.getOuterData(); }
125      
126      /** A Noop, because arrays shouldn't have outer data */
127      public void setOuterData(Data outerData) {
128    //    _elementType.setOuterData(outerData);
129      }
130        
131      public boolean equals(Object obj) {
132        if (this == obj) return true;
133        if (obj == null) return false;
134        if (obj.getClass() != this.getClass()) return false;
135    
136        ArrayData ad = (ArrayData) obj;    
137        
138        //For 2 array datas to be equal, all their symbolData fields must be equal, and their element types must be equal
139        return super.equals(obj) && getElementType().equals(ad.getElementType());
140      }
141      
142      /** Provides a hashcode method that distinguishes between array datas based on name */
143      public int hashCode() { return getName().hashCode(); }
144      
145      /** Returns true only under the following conditions: 
146       *  if assignTo is a class, assignTo must be java.lang.Object.
147       *  if assignTo is an interferface, then it must be Serializable or Clonable.
148       *  if assignTo is an array, then if this's element type is a primitive assignTo must have the same primitive element
149       *    type and
150       *  if this's element type is a reference type, this's reference type must be assignable to assignTo's element type.
151       */
152      public boolean isAssignableTo(SymbolData assignTo, JavaVersion version) {
153        if (assignTo instanceof ArrayData) {
154          if (this.getElementType().isPrimitiveType()) {
155            return this.getElementType() == ((ArrayData)assignTo).getElementType();
156          }
157          else if (((ArrayData)assignTo).getElementType().isPrimitiveType()) {
158            return false;
159          }
160          else {
161            return this.getElementType().isAssignableTo(((ArrayData)assignTo).getElementType(), version);
162          }
163        }
164        else {
165          return this.isSubClassOf(assignTo);
166        }
167      }
168      
169      /** Return true iff
170        * castTo is a class type and is Object
171        * castTo is an interface type that is Serializable or Clonable
172        * castTo is an array type then this and castTo must have element types that are either the same primitive type or
173        *   (both reference types and this's element type must be castable to castTo's element type)
174        */
175      public boolean isCastableTo(SymbolData castTo, JavaVersion version) {
176        if (castTo instanceof ArrayData) {
177          if (this.getElementType().isPrimitiveType()) {
178            return this.getElementType() == ((ArrayData)castTo).getElementType();
179          }
180          else if (((ArrayData)castTo).getElementType().isPrimitiveType()) {
181            return false;
182          }
183          else {
184            return this.getElementType().isCastableTo(((ArrayData)castTo).getElementType(), version);
185          }
186        }
187        else if (this.isSubClassOf(castTo)) {
188          return true;
189        }
190        else {
191          return this.isSubClassOf(castTo);
192        }
193      }
194      
195      /**
196       * Return the dimensions of this array (the level of nesting until a non-array element type is found).
197       */
198      public int getDimensions() {
199        int dim = 1;
200        SymbolData curData = this.getElementType();
201        while(curData instanceof ArrayData) {
202          dim ++;
203          curData = ((ArrayData) curData).getElementType();
204        }
205        return dim;
206      }
207      
208    
209       /**
210        * Test the methods in the enclosing class.  There is a test method corresponding to almost every method defined above.
211        */
212      public static class ArrayDataTest extends TestCase {
213        
214        private ArrayData _ad;
215        
216        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
217        private ModifiersAndVisibility _protectedMav = 
218          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});
219        private ModifiersAndVisibility _privateMav = 
220          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
221        private ModifiersAndVisibility _packageMav = 
222          new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
223        private ModifiersAndVisibility _abstractMav = 
224          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"abstract"});
225        private ModifiersAndVisibility _finalMav = 
226          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"final"});
227        private ModifiersAndVisibility _publicFinalMav = 
228          new ModifiersAndVisibility(SourceInfo.NONE, new String[]{"public", "final"});
229        private ModifiersAndVisibility _privateFinalMav =
230          new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private", "final"});
231        
232        private LanguageLevelVisitor llv;
233        private SourceInfo si;
234        
235        public ArrayDataTest() { this(""); }
236        public ArrayDataTest(String name) { super(name); }
237        
238        public void setUp() {
239          llv = new LanguageLevelVisitor(new File(""), 
240                                         "",
241                                         null,
242                                         new LinkedList<String>(), 
243                                         new LinkedList<String>(),
244                                         new HashSet<String>(), 
245                                         new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
246                                         new LinkedList<Command>());
247          
248          LanguageLevelConverter.symbolTable.clear();
249          LanguageLevelConverter._newSDs.clear();
250          si = SourceInfo.NONE;
251          SymbolData e = new SymbolData("elementType");
252          e.setIsContinuation(false);
253          _ad = new ArrayData(e, llv, si);
254          LanguageLevelVisitor.errors = new LinkedList<Pair<String, JExpressionIF>>();
255        }
256        
257        public void testGetDimensions() {
258          ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
259          intArray.setIsContinuation(false);
260          llv.symbolTable.remove("int[]");
261          llv.symbolTable.put("int[]", intArray);
262          
263          ArrayData intArrayArray = new ArrayData(intArray, llv, si);
264          intArrayArray.setIsContinuation(false);
265          llv.symbolTable.put("int[][]", intArrayArray);
266    
267          ArrayData intArray3 = new ArrayData(intArrayArray, llv, si);
268          intArray3.setIsContinuation(false);
269          llv.symbolTable.put("int[][][]", intArray3);
270          
271          assertEquals("Should return 1", 1, intArray.getDimensions());
272          assertEquals("Should return 2", 2, intArrayArray.getDimensions());
273          assertEquals("Should return 3", 3, intArray3.getDimensions());
274        }
275        
276        public void testGetPackage() {
277          SymbolData sd = _ad.getElementType();
278          sd.setPackage("a.b");
279          assertEquals("Should return a.b", "a.b", _ad.getPackage());
280          
281          sd.setPackage("");
282          assertEquals("Should return empty string", "", _ad.getPackage());
283          
284          sd.setPackage("who.let");
285          assertEquals("Should return who.let", "who.let", _ad.getPackage());
286        }
287        
288        public void testSetPackage() {
289          SymbolData sd = _ad.getElementType();
290          _ad.setPackage("a.b");
291          assertEquals("Should return a.b", "a.b", sd.getPackage());
292          
293          _ad.setPackage("");
294          assertEquals("Should return empty string", "", sd.getPackage());
295          
296          _ad.setPackage("who.let");
297          assertEquals("Should return who.let", "who.let", sd.getPackage());
298          
299        }
300    
301        public void testGetMav() {
302          SymbolData sd = _ad.getElementType();
303    
304          sd.setMav(_publicMav);
305          assertEquals("Should return _publicFinal mav", _publicFinalMav, _ad.getMav());
306          
307          sd.setMav(_privateMav);
308          assertEquals("Should return _privateFinal mav", _privateFinalMav, _ad.getMav());
309          
310          sd.setMav(_packageMav);
311          assertEquals("Should return _finalMav", _finalMav, _ad.getMav());
312          
313          sd.setMav(_publicFinalMav);
314          assertEquals("Should return _publicFinalMav", _publicFinalMav, _ad.getMav());
315        }
316    
317        public void testSetMav() {
318          SymbolData sd = _ad.getElementType();
319          
320          _ad.setMav(_publicMav);
321          assertEquals("Should return _publicMav", _publicMav, sd.getMav());
322          
323          _ad.setMav(_privateMav);
324          assertEquals("Should return _privateMav", _privateMav, sd.getMav());
325          
326          _ad.setMav(_publicFinalMav);
327          assertEquals("Should return _publicFinalMav", _publicFinalMav, _ad.getMav());
328          
329        }
330        
331        
332        public void testIsAssignableTo() {
333          //if assignTo is a class, it must be java.lang.Object
334          SymbolData object = llv.symbolTable.get("java.lang.Object");
335          assertTrue(_ad.isAssignableTo(object, JavaVersion.JAVA_5));
336          assertTrue(_ad.isAssignableTo(object, JavaVersion.JAVA_1_4));
337          
338          SymbolData notObject = new SymbolData("somethingRandom");
339          assertFalse(_ad.isAssignableTo(notObject, JavaVersion.JAVA_5));
340          assertFalse(_ad.isAssignableTo(notObject, JavaVersion.JAVA_1_4));
341          
342          //if assignTo is an interface, then it must be Serializable or Clonable
343          SymbolData serializable = _ad.getInterfaces().get(0);
344          SymbolData clonable = _ad.getInterfaces().get(1);
345          notObject.setInterface(true);
346          
347          assertTrue(_ad.isAssignableTo(serializable, JavaVersion.JAVA_5));
348          assertTrue(_ad.isAssignableTo(serializable, JavaVersion.JAVA_1_4));
349          assertTrue(_ad.isAssignableTo(clonable, JavaVersion.JAVA_5));
350          assertTrue(_ad.isAssignableTo(clonable, JavaVersion.JAVA_1_4));
351          assertFalse(_ad.isAssignableTo(notObject, JavaVersion.JAVA_5));
352          assertFalse(_ad.isAssignableTo(notObject, JavaVersion.JAVA_1_4));
353    
354          //if array is an array of primatives, then assign to must have primitive types that must match exactly
355          _ad = new ArrayData(SymbolData.INT_TYPE, llv, si);
356          ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
357          ArrayData doubleArray = new ArrayData(SymbolData.DOUBLE_TYPE, llv, si);
358          ArrayData charArray = new ArrayData(SymbolData.CHAR_TYPE, llv, si);
359          ArrayData objArray = new ArrayData(object, llv, si);
360          
361          assertTrue(_ad.isAssignableTo(intArray, JavaVersion.JAVA_5));
362          assertTrue(_ad.isAssignableTo(intArray, JavaVersion.JAVA_1_4));
363          assertFalse(_ad.isAssignableTo(charArray, JavaVersion.JAVA_5));
364          assertFalse(_ad.isAssignableTo(charArray, JavaVersion.JAVA_1_4));
365          assertFalse(_ad.isAssignableTo(doubleArray, JavaVersion.JAVA_5));
366          assertFalse(_ad.isAssignableTo(doubleArray, JavaVersion.JAVA_1_4));
367          assertFalse(_ad.isAssignableTo(objArray, JavaVersion.JAVA_5));
368          assertFalse(_ad.isAssignableTo(objArray, JavaVersion.JAVA_1_4));
369    
370          
371          //if array is an array of reference types, then reference types must be assignable to element type
372          SymbolData integerSd = new SymbolData("java.lang.Integer");
373          integerSd.setSuperClass(object);
374          _ad = new ArrayData(integerSd, llv, si);
375          notObject.setInterface(false);
376          ArrayData randomArray = new ArrayData(notObject, llv, si);
377    
378          assertTrue(_ad.isAssignableTo(objArray, JavaVersion.JAVA_5));
379          assertTrue(_ad.isAssignableTo(objArray, JavaVersion.JAVA_1_4));
380          assertTrue(_ad.isAssignableTo(_ad, JavaVersion.JAVA_5));
381          assertTrue(_ad.isAssignableTo(_ad, JavaVersion.JAVA_1_4));
382          assertFalse(_ad.isAssignableTo(randomArray, JavaVersion.JAVA_5));
383          assertFalse(_ad.isAssignableTo(randomArray, JavaVersion.JAVA_1_4));
384          assertFalse(_ad.isAssignableTo(intArray, JavaVersion.JAVA_5));
385          assertFalse(_ad.isAssignableTo(intArray, JavaVersion.JAVA_1_4));
386        }
387       
388        
389        public void testIsCastableTo() {
390          //if castTo is a class type and Object, should work
391          SymbolData object = llv.symbolTable.get("java.lang.Object");
392          assertTrue(_ad.isCastableTo(object, JavaVersion.JAVA_5));
393          assertTrue(_ad.isCastableTo(object, JavaVersion.JAVA_1_4));
394          
395          //if castTo is an interface, then it must be Serializable or Clonable
396          SymbolData serializable = _ad.getInterfaces().get(0);
397          SymbolData clonable = _ad.getInterfaces().get(1);
398          
399          assertTrue(_ad.isAssignableTo(serializable, JavaVersion.JAVA_5));
400          assertTrue(_ad.isAssignableTo(serializable, JavaVersion.JAVA_1_4));
401          assertTrue(_ad.isAssignableTo(clonable, JavaVersion.JAVA_5));
402          assertTrue(_ad.isAssignableTo(clonable, JavaVersion.JAVA_1_4));
403    
404          //anything non-array type should break
405          SymbolData notObject = new SymbolData("somethingRandom");
406          assertFalse(_ad.isCastableTo(notObject, JavaVersion.JAVA_5));
407          assertFalse(_ad.isCastableTo(notObject, JavaVersion.JAVA_1_4));
408    
409          
410         
411          //if castTo is an array and the array elements can be cast to its elements, should work
412          
413          //if array is an array of primatives, then assign to must have primitive types that must match exactly
414          _ad = new ArrayData(SymbolData.INT_TYPE, llv, si);
415          ArrayData intArray = new ArrayData(SymbolData.INT_TYPE, llv, si);
416          ArrayData doubleArray = new ArrayData(SymbolData.DOUBLE_TYPE, llv, si);
417          ArrayData charArray = new ArrayData(SymbolData.CHAR_TYPE, llv, si);
418          ArrayData objArray = new ArrayData(object, llv, si);
419          
420          assertTrue(_ad.isCastableTo(intArray, JavaVersion.JAVA_5));
421          assertTrue(_ad.isCastableTo(intArray, JavaVersion.JAVA_1_4));
422          assertFalse(_ad.isCastableTo(charArray, JavaVersion.JAVA_5));
423          assertFalse(_ad.isCastableTo(charArray, JavaVersion.JAVA_1_4));
424          assertFalse(_ad.isCastableTo(doubleArray, JavaVersion.JAVA_5));
425          assertFalse(_ad.isCastableTo(doubleArray, JavaVersion.JAVA_1_4));
426          assertFalse(_ad.isCastableTo(objArray, JavaVersion.JAVA_5));
427          assertFalse(_ad.isCastableTo(objArray, JavaVersion.JAVA_1_4));
428    
429          
430          //if array is an array of reference types, then reference types must be castable to element type
431          SymbolData integerSd = new SymbolData("java.lang.Integer");
432          integerSd.setSuperClass(object);
433          _ad = new ArrayData(integerSd, llv, si);
434          notObject.setInterface(false);
435          ArrayData randomArray = new ArrayData(notObject, llv, si);
436    
437          assertTrue(_ad.isCastableTo(objArray, JavaVersion.JAVA_5));
438          assertTrue(_ad.isCastableTo(objArray, JavaVersion.JAVA_1_4));
439          assertTrue(_ad.isCastableTo(_ad, JavaVersion.JAVA_5));
440          assertTrue(_ad.isCastableTo(_ad, JavaVersion.JAVA_1_4));
441          assertFalse(_ad.isCastableTo(randomArray, JavaVersion.JAVA_5));
442          assertFalse(_ad.isCastableTo(randomArray, JavaVersion.JAVA_1_4));
443          assertFalse(_ad.isCastableTo(intArray, JavaVersion.JAVA_5));
444          assertFalse(_ad.isCastableTo(intArray, JavaVersion.JAVA_1_4));
445    
446          _ad = new ArrayData(object, llv, si);
447          assertTrue(_ad.isCastableTo(new ArrayData(integerSd, llv, si), JavaVersion.JAVA_5));
448          assertTrue(_ad.isCastableTo(new ArrayData(integerSd, llv, si), JavaVersion.JAVA_1_4));
449          assertFalse(_ad.isCastableTo(new ArrayData(SymbolData.INT_TYPE, llv, si), JavaVersion.JAVA_5));
450          assertFalse(_ad.isCastableTo(new ArrayData(SymbolData.INT_TYPE, llv, si), JavaVersion.JAVA_1_4));
451        }
452      }
453    }