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    }