001 /*BEGIN_COPYRIGHT_BLOCK
002 *
003 * Copyright (c) 2001-2008, 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 java.util.*;
041 import junit.framework.TestCase;
042 import edu.rice.cs.javalanglevels.parser.JExprParser;
043
044 /** Abstract class epresenting the data for a given braced body: a class, interface, method, or just a body. */
045 public abstract class Data {
046
047 /** The fully qualified name of this data. */
048 protected String _name;
049
050 /** The vars defined in the lexical scope of this data.*/
051 protected LinkedList<VariableData> _vars;
052
053 /** All enclosing data are in this list. */
054 protected LinkedList<Data> _enclosingData;
055
056 /** The modifiers and visibility of this data.*/
057 protected ModifiersAndVisibility _modifiersAndVisibility;
058
059 /** The outer data--what directly encloses this data.*/
060 protected Data _outerData;
061
062 /** Any inner classes that are defined in this data.*/
063 protected LinkedList<SymbolData> _innerClasses;
064
065 /** All blocks defined within this data, in lexical order.*/
066 protected LinkedList<BlockData> _blocks;
067
068 /** Iterator over _blocks*/
069 protected Iterator<BlockData> _blockIterator;
070
071 /** The default constructor for a Data. It takes in the outerData, and sets all lists and the name to empty, except
072 * that the outer data is asdded to the enclosing data list. */
073 public Data(Data outerData) {
074 _name = "";
075 _modifiersAndVisibility = null;
076 _vars = new LinkedList<VariableData>();
077 _enclosingData = new LinkedList<Data>();
078 _outerData = outerData;
079 if (outerData != null) {
080 _enclosingData.addLast(_outerData); // We add superclasses and interfaces to the front of this _enclosingData.
081 }
082 _innerClasses = new LinkedList<SymbolData>();
083 _blocks = new LinkedList<BlockData>();
084 _blockIterator = null;
085 }
086
087 /** @return the name of this data.*/
088 public String getName() { return _name; }
089
090 /** Set the fully qualified name of this data.
091 * @param name The new fully qualified name of this data.
092 */
093 void setName(String name) { _name = name; }
094
095 public Boolean isAnonymousClass() {
096 int lastIndex = _name.lastIndexOf('$');
097 try { return (lastIndex < 0) && Integer.parseInt(_name.substring(lastIndex+1)) >= 0; }
098 catch(NumberFormatException e) { return false; /* suffix is not an anonymous class index */ }
099 }
100
101 public Boolean isDoublyAnonymous() {
102 if (! isAnonymousClass()) return false;
103 for (Data d: getEnclosingData()) {
104 if (d.isAnonymousClass()) return true;
105 }
106 return false;
107 }
108
109 /** Set vars to the specified linked list of vars, the variables that are defined in the scope of this data. */
110 void setVars(LinkedList<VariableData> vars) { _vars = vars; }
111
112 /** Finds and returns the particular VariableData declared in this Data's context.
113 * @param name Name of the variable
114 * @return The VariableData with the matching name or null if it was not found.
115 */
116 public VariableData getVar(String name) {
117 Iterator<VariableData> iter = _vars.iterator();
118 while (iter.hasNext()) {
119 VariableData vd = iter.next();
120 if (vd.getName().equals(name)) {
121 return vd;
122 }
123 }
124 return null;
125 }
126
127 /**@return the list of variables declared in the scope of this data.*/
128 public LinkedList<VariableData> getVars() { return _vars; }
129
130 /** @return the list of enclosing data. */
131 public LinkedList<Data> getEnclosingData() { return _enclosingData; }
132
133 /** Add to the front because we want the outer data to be the last thing in the list. */
134 public void addEnclosingData(Data enclosingData) {
135 assert enclosingData != null;
136 if (!_enclosingData.contains(enclosingData)) {
137 _enclosingData.addFirst(enclosingData);
138 }
139 }
140
141 //Used during testing
142 public void setEnclosingData(LinkedList<Data> d) { _enclosingData = d; }
143
144 /** Check to see if a variable with the same name as vr has already been defined in the scope of this data. If so,
145 * return true. Otherwise, return false.
146 * @param vr The VariableData whose name we are searching for.
147 * @return true if that name has already been used in this scope, false otherwise.
148 */
149 private boolean _repeatedName (VariableData vr) {
150 Iterator<VariableData> iter = _vars.iterator();
151 while (iter.hasNext()) {
152 VariableData next = iter.next();
153 if (vr.getName().equals(next.getName())) {
154 return true;
155 }
156 }
157 return false;
158 }
159
160 /** Add the specified Variable Data to the list of variables defined in this scope, unless its name has already been
161 * used. Return true if it was successfully added, and false otherwise.
162 * @param var The variable we want to add to this scope.
163 * @return true if it was successfully added, false otherwise.
164 */
165 public boolean addVar(VariableData var) {
166 if (! _repeatedName(var)) {
167 _vars.addLast(var);
168 return true;
169 }
170 else return false;
171 }
172
173 /** Add the array of variable datas to the list of variables defined in this scope, unless a name has already been
174 * used. Return true if all variables were added successfully, false otherwise.
175 * @param vars The VariableData[] that we want to add.
176 * @return true if all VariableDatas were added successfully, false otherwise.
177 */
178 public boolean addVars(VariableData[] vars) {
179 boolean success = true;
180 for (int i = 0; i < vars.length; i++) {
181 // if (vars[i] == null) { System.out.println("Var " + i + " was null!"); }
182 if (! _repeatedName(vars[i])) {
183 _vars.addLast(vars[i]);
184 }
185 else success = false;
186 }
187 return success;
188 }
189
190 /** Add the array of variable datas to the list of variables defined in this scope, unless a name has already been
191 * used. Return true if all variables were added successfully, false otherwise. Set each of the variable datas in
192 * the array to be final before adding them.
193 * @param vars the VariableData[] that we want to add.
194 * @return true if all VariableDatas were added successfully, false otherwise.
195 */
196 public boolean addFinalVars(VariableData[] vars) {
197 boolean success = true;
198 for (int i = 0; i < vars.length; i++) {
199 if (! _repeatedName(vars[i])) {
200 if (! vars[i].isFinal()) vars[i].setFinal();
201 _vars.addLast(vars[i]);
202 }
203 else { success = false; }
204 }
205 return success;
206 }
207
208 /** @return the modifiersAndVisibility for this data. */
209 public ModifiersAndVisibility getMav() { return _modifiersAndVisibility; }
210
211 /** Assigns the specified modifiersAndVisiblity to this data.
212 * @param modifiersAndVisibility The ModifiersAndVisibility to assign to this data.
213 */
214 public void setMav(ModifiersAndVisibility modifiersAndVisibility) {
215 _modifiersAndVisibility = modifiersAndVisibility;
216 }
217
218 /**Return the enclosing getSymbolData()*/
219 public abstract SymbolData getSymbolData();
220
221 /** @return the directly enclosing outer data. */
222 public Data getOuterData() { return _outerData; }
223
224 /** Sets the outer data to the specified value--throw an exception if the data already has an outer data.
225 * @param outerData The Data that encloses this data.
226 */
227 public void setOuterData(Data outerData) {
228 if (outerData == null) {
229 assert _outerData == null; // Client code should not try to nullify a defined outerData value
230 return;
231 }
232 if (_outerData == null || _outerData.equals(outerData)) {
233 _outerData = outerData;
234 if (! _enclosingData.contains(outerData)) _enclosingData.addLast(outerData);
235 }
236 else {
237 throw new RuntimeException("Internal Program Error: Trying to reset an outer data to " + outerData.getName() +
238 " for " + getName() + " that has already been set to " + _outerData.getName() +
239 ". Please report this bug.");
240 }
241 }
242
243 /** @return true if d is an outer data of this data. TODO: What if d is a library class? */
244 public boolean isOuterData(Data d) {
245 Data outerData = _outerData;
246 while ((outerData != null) && ! LanguageLevelVisitor.isJavaLibraryClass(outerData.getName())) {
247 if (outerData == d) return true;
248 outerData = outerData.getOuterData();
249 }
250 return false;
251 }
252
253 /** @return the enclosing class of this. */
254 public SymbolData getEnclosingClass() {
255 Data next = _outerData;
256
257 while (next != null) {
258 if (next instanceof SymbolData) return (SymbolData) next;
259 next = next.getOuterData();
260 }
261 return null;
262 }
263
264 /** Loop over the specified string, and replace any '$' with '.' This is used to change an inner class name to a
265 * standard format. It fails if the inner class is local or anonymous!
266 * @param s The String to change.
267 * @return The converted string.
268 */
269 public static String dollarSignsToDots(String s) { return s.replace('$', '.'); }
270
271 /** Loop over the specified string, and replace any '.' with '$' This is used to change an inner class name from
272 * external (as in Java source) to internal (as in class files) format.
273 * @param s The String to change.
274 * @return The converted string.
275 */
276 public static String dotsToDollarSigns(String s) { return s.replace('.', '$'); }
277
278 /** Determines the name of the next anonymous inner class (enclosing class name + '$' + sequence number). Looks
279 * through the list of inner classes of this data to see if there is a match. (It should succeed).
280 * @return the SymbolData for next anonymous inner class of this data; null if it cannot be found
281 */
282 public SymbolData getNextAnonymousInnerClass() {
283 String name = getSymbolData().getName() + '$' + getSymbolData().preincrementAnonymousInnerClassNum();
284 // System.err.println("**** Looking up anonymous inner class " + name);
285 LinkedList<SymbolData> myDatas = getInnerClasses();
286 SymbolData myData = null;
287 //look through the inner classes for the data
288 for (int i = 0; i < myDatas.size(); i++) {
289 if (myDatas.get(i).getName().equals(name)) {
290 myData = myDatas.get(i);
291 break;
292 }
293 }
294 return myData;
295 }
296
297 /** Reset the block iterator to the beginning of the list of blocks. */
298 public void resetBlockIterator() { _blockIterator = null; }
299
300 /** Returns the next block contained within this data.
301 * @return a BlockData, or null if none exists.
302 */
303 public BlockData getNextBlock() {
304 if (_blockIterator == null) { _blockIterator = _blocks.iterator(); }
305
306 if (_blockIterator.hasNext()) { return _blockIterator.next(); }
307 else { return null; }
308 }
309
310 /** Add a BlockData to this Data's list of blocks. */
311 public void addBlock(BlockData b) { _blocks.add(b); }
312
313 /** Remove all blocks from this data's list of enclosed blocks. (Used to simplify testing.) */
314 public void removeAllBlocks() { _blocks.clear(); }
315
316 /** Takes in a relative name and tries to match it with one of this Data's inner classes or inner interfaces. The
317 * relName argument is a name relative to this SymbolData (such as B to request the the class with this
318 * relative name within some enclosing symbol data or B$C to request the class A.B.C from class A) and
319 * may be delimited by '.' or '$' (??). If the name is not found in this Data, checks the outer data (if there is
320 * one), which will recursively search up the chain of enclosing Datas. If no matching visible inner classes or
321 * interfaces are found, but one or more that are not visible are found, one of the non-visible ones will be
322 * returned. This means that checkAccess should be called after this method.
323 * TODO: Is support for '$' delimiter required to process inner classes in class files? Yes.
324 * !!! Eliminate the kludge in this method.
325 * @param relName The name of the inner class or interface to find RELATIVE to this SymbolData
326 * @return The SymbolData for the matching inner class or interface is null if there isn't one.
327 */
328 public SymbolData getInnerClassOrInterface(String relName) {
329 // if (relName.equals("MyInner")) System.err.println("getInnerClass('" + relName + "') called on '" + this + "'");
330 int firstIndexOfDot = relName.indexOf('.');
331 int firstIndexOfDollar = relName.indexOf("$");
332 if (firstIndexOfDot == -1) firstIndexOfDot = firstIndexOfDollar;
333 else if (firstIndexOfDollar >= 0 && firstIndexOfDollar < firstIndexOfDot) firstIndexOfDot = firstIndexOfDollar;
334
335 // First, look through the inner classes/interfaces of this class
336 SymbolData privateResult = null;
337 SymbolData result = getInnerClassOrInterfaceHelper(relName, firstIndexOfDot);
338 if (relName.equals("MyInner")) {
339 // System.err.println("getInnerClassOrInterfaceHelper('" + relName + "', " + firstIndexOfDot + ")");
340 // System.err.println("_innerClasses = " + _innerClasses);
341 // System.err.println("Result is: '" + result + "'");
342 }
343 if (result != null) {
344 // System.err.println("Result is: '" + result + "'");
345 SymbolData outerPiece;
346 if (firstIndexOfDot > 0) {
347 outerPiece = getInnerClassOrInterfaceHelper(relName.substring(0, firstIndexOfDot), -1);
348 }
349 else { outerPiece = result; }
350 if (TypeChecker.checkAccess(outerPiece.getMav(), outerPiece, getSymbolData())) return result;
351 else {
352 privateResult = result;
353 result = null;
354 }
355 }
356
357 // Call this method recursively on the outer data; anything our outer class can see we can see, so there is no
358 // eason to check accessibility here
359 if (_outerData != null) {
360 result = _outerData.getInnerClassOrInterface(relName);
361 // if (relName.equals("MyInner")) System.err.println("outerResult = " + result);
362 if (result != null) return result;
363 }
364
365 return privateResult;
366 }
367
368 /** Takes in a relative name and tries to match it with one of this Data's inner classes or inner interfaces. The
369 * relName argument is a name relative to this SymbolData (such as B.C to request the class A.B.C from class A) and
370 * may be delimited by '.' or '$'. This method is overridden in SymbolData (but not other concrete Data classes) to
371 * handle the fact that classes must check their super classes and interfaces and interfaces must check their super
372 * interfaces.
373 * TODO: Kludge! Only use dots to separate segments!!!
374 * @return The SymbolData for the matching inner class or interface or null if there isn't one.
375 */
376 protected SymbolData getInnerClassOrInterfaceHelper(String relName, int firstIndexOfDot) {
377 Iterator<SymbolData> iter = innerClassesAndInterfacesIterator();
378 while (iter.hasNext()) {
379 SymbolData sd = iter.next();
380 String sdName = sd.getName();
381
382 sdName = LanguageLevelVisitor.getUnqualifiedClassName(sdName);
383 // if (sdName.equals("MyInner")) System.err.println("In getInnerClass, sdName = '" + sdName + "'; relName = '"
384 // + relName +"'");
385 if (firstIndexOfDot == -1) {
386 if (sdName.equals(relName)) return sd;
387 }
388 else {
389 if (sdName.equals(relName.substring(0, firstIndexOfDot))) {
390 return sd.getInnerClassOrInterface(relName.substring(firstIndexOfDot + 1));
391 }
392 }
393 }
394 return null;
395 }
396
397 public Iterator<SymbolData> innerClassesAndInterfacesIterator() { return _innerClasses.iterator(); }
398
399 /** @return The inner classes of this Data. */
400 public LinkedList<SymbolData> getInnerClasses() { return _innerClasses; }
401
402 /** Sets the inner classes of this Data. */
403 public void setInnerClasses(LinkedList<SymbolData> innerClasses) { _innerClasses = innerClasses; }
404
405 /** Add the specified SymbolData to the end of the list of inner classes.
406 * @param innerClass The SymbolData to add.
407 */
408 public void addInnerClass(SymbolData innerClass) { _innerClasses.addLast(innerClass); }
409
410 /** @return true if this data has the specified String modifier, and false otherwise. */
411 public boolean hasModifier(String modifier) {
412 if (getMav() == null) {return false;}
413 String[] mavStrings = _modifiersAndVisibility.getModifiers();
414 for (int i = 0; i < mavStrings.length; i++) {
415 if (mavStrings[i].equals(modifier)) {
416 return true;
417 }
418 }
419 return false;
420 }
421
422 //TODO: now that we have this, can we factor out some code in VariableData?
423 /** Add the specified modifier to the modifiers and visibility for this data, if it is not already present.
424 * @param modifier The String to add.
425 */
426 public void addModifier(String modifier) {
427 if (! hasModifier(modifier)) {
428 if (_modifiersAndVisibility == null) { setMav(new ModifiersAndVisibility(SourceInfo.NONE, new String[0])); }
429 String[] modifiers = _modifiersAndVisibility.getModifiers();
430 String[] newModifiers = new String[modifiers.length + 1];
431 newModifiers[0] = modifier;
432 for (int i = 1; i <= modifiers.length; i++) {
433 newModifiers[i] = modifiers[i-1];
434 }
435 _modifiersAndVisibility = new ModifiersAndVisibility(_modifiersAndVisibility.getSourceInfo(), newModifiers);
436 }
437 }
438
439 /** Check if varName is used in this Data's scope. If so, find a new name for the variable by appending a counter to
440 * its name until an unused variable name results. Return the new name.
441 * @param varName The initial String name of the variable we are creating.
442 * @return The new variable name which does not shadow anything in vars.
443 */
444 public String createUniqueName(String varName) {
445 VariableData vd =
446 TypeChecker.getFieldOrVariable(varName, this, getSymbolData(), new NullLiteral(SourceInfo.NONE), getVars(),
447 true, false);
448 String newName = varName;
449 int counter = 0; // Note: counter overflow is effectively impossible; 2G anonymous classes would blow memory
450 while (vd != null && counter != -1) {
451 newName = varName + counter; counter++;
452 vd = TypeChecker.getFieldOrVariable(newName, this, getSymbolData(), new NullLiteral(SourceInfo.NONE),
453 getVars(), true, false);
454 }
455 if (counter == -1) { throw new RuntimeException("Internal Program Error: Unable to rename variable " + varName
456 + ". All possible names were taken. Please report this bug");}
457 return newName;
458 }
459
460
461 /** Test the methods in the above class. */
462 public static class DataTest extends TestCase {
463
464 private Data _d;
465
466 private ModifiersAndVisibility _publicMav =
467 new ModifiersAndVisibility(SourceInfo.NONE, new String[] { "public" });
468 private ModifiersAndVisibility _staticMav =
469 new ModifiersAndVisibility(SourceInfo.NONE, new String[] { "static" });
470 private ModifiersAndVisibility _lotsaMav =
471 new ModifiersAndVisibility(SourceInfo.NONE, new String[] { "public", "final", "abstract" });
472 private ModifiersAndVisibility _protectedMav =
473 new ModifiersAndVisibility(SourceInfo.NONE, new String[] { "protected" });
474 private ModifiersAndVisibility _privateMav =
475 new ModifiersAndVisibility(SourceInfo.NONE, new String[] { "private" });
476
477 public DataTest() { this("");}
478 public DataTest(String name) { super(name); }
479
480 public void testRepeatedName() {
481 _d = new SymbolData("myname");
482
483 VariableData vd = new VariableData("v1", _publicMav, SymbolData.INT_TYPE, false, _d);
484
485 // Compare a vd to an symbol data with no vars
486 assertFalse("No variables to repeat name", _d._repeatedName(vd));
487
488 // Compare a vd to a symbol data with 1 var with a different name
489 _d.addVar(new VariableData("v2", _protectedMav, SymbolData.BOOLEAN_TYPE, true, _d));
490 assertFalse("No repeated name", _d._repeatedName(vd));
491
492 // Compare a vd to a symbol data who has a var with the same name
493 _d.addVar(vd);
494 assertTrue("Should be repeated name", _d._repeatedName(vd));
495 // System.err.println("testRepeatedName finished");
496 }
497
498 public void testIsAbstract() {
499 _d = new SymbolData("myName");
500 _d.setMav(_publicMav);
501
502 // not abstract
503 assertFalse("Should not be abstract", _d.hasModifier("abstract"));
504
505 _d.setMav(_lotsaMav);
506
507 // abstract
508 assertTrue("Should be abstract", _d.hasModifier("abstract"));
509
510 // System.err.println("testIsAbstract finished");
511 }
512
513 public void testAddVar() {
514 _d = new SymbolData("myName");
515 VariableData vd = new VariableData("v1", _publicMav, SymbolData.INT_TYPE, true, _d);
516 VariableData vd2 = new VariableData("v2", _publicMav, SymbolData.CHAR_TYPE, true, _d);
517 LinkedList<VariableData> myVds = new LinkedList<VariableData>();
518 myVds.addLast(vd);
519
520 //first variable added
521 assertTrue("Should be able to add first variable", _d.addVar(vd));
522 assertEquals("Variable list should have 1 variable, vd", myVds, _d.getVars());
523
524 //duplicate variable
525 assertFalse("Should not be able to add same variable again", _d.addVar(vd));
526 assertEquals("Variable list should not have changed", myVds, _d.getVars());
527
528 //different variable
529 myVds.addLast(vd2);
530 assertTrue("Should be able to add a different variable", _d.addVar(vd2));
531 assertEquals("Variable list should have 2 variables, vd, vd2", myVds, _d.getVars());
532
533 // System.err.println("testAddVar finished");
534
535 }
536
537 public void testAddVars() {
538 _d = new SymbolData("genius");
539 VariableData vd = new VariableData("v1", _publicMav, SymbolData.INT_TYPE, true, _d);
540 VariableData vd2 = new VariableData("v2", _publicMav, SymbolData.CHAR_TYPE, true, _d);
541 VariableData[] toAdd = new VariableData[] {vd, vd2};
542 LinkedList<VariableData> myVds = new LinkedList<VariableData>();
543
544 //first adding array
545 myVds.addLast(vd);
546 myVds.addLast(vd2);
547 assertTrue("Should be able to add new vars array", _d.addVars(toAdd));
548 assertEquals("Variable list should have 2 variables", myVds, _d.getVars());
549
550 //trying to read array whose variables are already there.
551 assertFalse("Should not be able to add same variables again", _d.addVars(toAdd));
552 assertEquals("Variable list should not have changed", myVds, _d.getVars());
553
554 //trying to add a different array
555 VariableData vd3 = new VariableData("v3", _publicMav, SymbolData.INT_TYPE, true, _d);
556 VariableData[] toAdd2 = new VariableData[] {vd3};
557 myVds.addLast(vd3);
558
559 assertTrue("Should be able to add new variable array", _d.addVars(toAdd2));
560 assertEquals("Variable list should now have 3 variables", myVds, _d.getVars());
561
562 //try adding an empty array of variable datas
563 assertTrue("Should be able to add an empty array", _d.addVars(new VariableData[0]));
564 assertEquals("Variable list should not have changed by adding empty array", myVds, _d.getVars());
565
566 // System.err.println("testAddVars finished");
567 }
568
569 public void testGetVar() {
570 _d = new SymbolData("woah");
571 VariableData vd = new VariableData("v1", _publicMav, SymbolData.INT_TYPE, false, _d);
572 VariableData vd2 = new VariableData("v2", _publicMav, SymbolData.CHAR_TYPE, true, _d);
573 VariableData[] toAdd = new VariableData[] {vd, vd2};
574 _d.addVars(toAdd);
575
576 // Lookup a name that should be there
577 assertEquals("Should return vd", vd, _d.getVar("v1"));
578
579 // Lookup a name that should not be there
580 assertEquals("Should return null--no variable with that name", null, _d.getVar("whatever"));
581 // System.err.println("testGetVar finished");
582 }
583
584 public void testIsOuterData() {
585 _d = new SymbolData("asdf");
586 SymbolData d2 = new SymbolData("qwer");
587 SymbolData d246 = new SymbolData("fdsa");
588 d2.setOuterData(_d);
589 _d.setOuterData(d246);
590 assertTrue("d246 should be outer data of d2", d2.isOuterData(d246));
591 assertTrue("d246 should be outer data of _d", _d.isOuterData(d246));
592 assertFalse("d2 should not be outer data of d246", d246.isOuterData(d2));
593 // System.err.println("testIsOuterData finished");
594 }
595
596 public void testGetInnerClassOrInterface() {
597 SymbolData sd1 = new SymbolData("testing");
598 SymbolData sd2 = new SymbolData("testing$test123");
599 SymbolData sd3 = new SymbolData("testing$test123$test1234");
600 sd1.addInnerClass(sd2);
601 sd2.addInnerClass(sd3);
602
603 // One level can be found
604 SymbolData result = sd1.getInnerClassOrInterface("test123");
605 assertEquals("The correct inner SymbolData should be returned", sd2, result);
606
607 // Dollars or dots are okay, and nested inner classes can be found
608 result = sd2.getInnerClassOrInterface("test1234");
609 assertEquals("The correct nested inner SymbolData should be returned", sd3, result);
610
611 //dollars or dots are okay, and nested inner classes can be found
612 result = sd1.getInnerClassOrInterface("test123.test1234");
613 assertEquals("The correct nested inner SymbolData should be returned", sd3, result);
614
615 // Dollars or dots are okay, and nested inner classes can be found
616 result = sd1.getInnerClassOrInterface("test123$test1234");
617 assertEquals("The correct nested inner SymbolData should be returned", sd3, result);
618
619 // null is returned when a non-present inner class is looked for.
620 result = sd1.getInnerClassOrInterface("testing.notYourInnerClass");
621 assertEquals("null should be returned", null, result);
622
623 SymbolData sd4 = new SymbolData("testing");
624 SymbolData sd5 = new SymbolData("testing$test123");
625 sd5.setInterface(true);
626 SymbolData sd6 = new SymbolData("testing$test123$2test1234");
627 sd4.addInnerInterface(sd5);
628 sd5.addInnerClass(sd6);
629
630 // One level can be found
631 result = sd4.getInnerClassOrInterface("test123");
632 assertEquals("The correct inner SymbolData should be returned", sd5, result);
633
634 //dollars or dots are okay, and nested inner classes can be found
635 result = sd5.getInnerClassOrInterface("test1234");
636 assertEquals("The correct nested inner SymbolData should be returned", sd6, result);
637
638 //dollars or dots are okay, and nested inner classes can be found
639 result = sd4.getInnerClassOrInterface("test123.test1234");
640 assertEquals("The correct nested inner SymbolData should be returned", sd6, result);
641
642 //null is returned when a non-present inner class is looked for.
643 result = sd4.getInnerClassOrInterface("testing.notYourInnerClass");
644 assertEquals("null should be returned", null, result);
645
646 //Test a class defined in the context of a method
647 SymbolData sd7 = new SymbolData("test123.myMethod$bob");
648 sd7.setIsContinuation(false);
649 MethodData md = new MethodData("myMethod", _publicMav, new TypeParameter[0],
650 SymbolData.INT_TYPE, new VariableData[0], new String[0], sd1,
651 new NullLiteral(SourceInfo.NONE));
652 md.addInnerClass(sd7);
653 assertEquals("Should return sd7", sd7, md.getInnerClassOrInterface("bob"));
654
655 //Test an ambiguous case, where the inner class is in both the super interface and the super class.
656 SymbolData interfaceInner = new SymbolData("MyInterface$MyInner");
657 interfaceInner.setIsContinuation(false);
658 interfaceInner.setInterface(true);
659
660 SymbolData superInner = new SymbolData("MySuper$MyInner");
661 superInner.setIsContinuation(false);
662
663 SymbolData myInterface = new SymbolData("MyInterface");
664 myInterface.setInterface(true);
665 myInterface.setIsContinuation(false);
666 myInterface.addInnerClass(interfaceInner);
667 interfaceInner.setOuterData(myInterface);
668
669 SymbolData mySuper = new SymbolData("MySuper");
670 mySuper.addInnerClass(superInner);
671 superInner.setOuterData(mySuper);
672
673 SymbolData me = new SymbolData("Me");
674 me.setSuperClass(mySuper);
675 me.addInterface(myInterface);
676
677 assertEquals("Should return SymbolData.AMBIGUOUS_REFERENCE", SymbolData.AMBIGUOUS_REFERENCE,
678 me.getInnerClassOrInterface("MyInner"));
679
680 // Test a case where the inner class is private in one, but not the other
681 superInner.setMav(_privateMav);
682 assertEquals("Should return interfaceInner", interfaceInner, me.getInnerClassOrInterface("MyInner"));
683
684 // Test a case where the inner class is private in both--returns one of them
685 interfaceInner.setMav(_privateMav);
686 assertEquals("Should return interfaceInner", interfaceInner, me.getInnerClassOrInterface("MyInner"));
687
688 // Test a case where the inner most class is private, but one layer is public
689 interfaceInner.setMav(_publicMav);
690 SymbolData innerInterfaceInner = new SymbolData("MyInterface$MyInner$Inner");
691 innerInterfaceInner.setMav(_privateMav);
692 interfaceInner.addInnerClass(innerInterfaceInner);
693 innerInterfaceInner.setOuterData(interfaceInner);
694 assertEquals("Should return innerInterfaceInner", innerInterfaceInner,
695 me.getInnerClassOrInterface("MyInner.Inner"));
696
697 // System.err.println("testGetInnerClassOrInterface finished");
698 }
699
700 public void testCreateUniqueName() {
701 // where varName is not defined
702 MethodData md = new MethodData("foozle", new VariableData[0]);
703 md.addVars(md.getParams());
704 md.setOuterData(new SymbolData("Fooz"));
705 String result = md.createUniqueName("avar");
706 assertEquals("the result is correct", "avar", result);
707
708 // where varName is defined in a method signature
709 VariableData vd = new VariableData("avar", _publicMav, SymbolData.INT_TYPE, true, null);
710 md = new MethodData("foozleWithAvar", new VariableData[] {vd});
711 vd.setEnclosingData(md);
712 md.addVars(md.getParams());
713 md.setOuterData(new SymbolData("Fooz"));
714 result = md.createUniqueName("avar");
715 assertEquals("the result is correct", "avar0", result);
716
717 // where varName & varName0 are defined in the class
718 SymbolData sd = new SymbolData("RandomClass");
719 VariableData vd0 = new VariableData("avar0", _publicMav, SymbolData.DOUBLE_TYPE, true, sd);
720 vd.setEnclosingData(sd);
721 sd.addVars(new VariableData[] {vd, vd0});
722 result = sd.createUniqueName("avar");
723 assertEquals("the result is correct", "avar1", result);
724
725 // where varName is defined in an enclosing class
726 sd = new SymbolData("RandomClass");
727 sd.setMav(_staticMav);
728 SymbolData sd2 = new SymbolData("IAteRandomClass");
729 sd2.addInnerClass(sd);
730 sd.setOuterData(sd2);
731 sd.addVar(vd);
732 result = sd.createUniqueName("avar");
733 assertEquals("the result is correct", "avar0", result);
734
735 // where varName is defined in a super class and enclosing class
736 SymbolData sd3 = new SymbolData("RandomsMama");
737 sd.setSuperClass(sd3);
738 sd3.addVar(vd0);
739 result = sd.createUniqueName("avar");
740 assertEquals("the result is correct", "avar1", result);
741 // System.err.println("testCreateName finished");
742 }
743
744 public void testGetNextAnonymousInnerClass() {
745 SymbolData sd1 = new SymbolData("silly");
746 sd1.setIsContinuation(false);
747
748 _d = new BlockData(sd1);
749
750 SymbolData anon1 = new SymbolData("silly$1");
751 anon1.setIsContinuation(false);
752 SymbolData anon2 = new SymbolData("silly$2");
753 anon2.setIsContinuation(false);
754 sd1.addInnerClass(anon1);
755 anon1.setOuterData(sd1);
756 _d.addInnerClass(anon2);
757 anon2.setOuterData(_d);
758
759 assertEquals("Should return anon1", anon1, sd1.getNextAnonymousInnerClass());
760 assertEquals("Should return anon2", anon2, _d.getNextAnonymousInnerClass());
761 assertEquals("Should return null", null, _d.getNextAnonymousInnerClass());
762 assertEquals("Should return null", null, sd1.getNextAnonymousInnerClass());
763
764 // System.err.println("testGetNextAnonymousInnerClass finished");
765 }
766 }
767 }