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 }