001    /*BEGIN_COPYRIGHT_BLOCK
002     *
003     * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
004     * All rights reserved.
005     * 
006     * Redistribution and use in source and binary forms, with or without
007     * modification, are permitted provided that the following conditions are met:
008     *    * Redistributions of source code must retain the above copyright
009     *      notice, this list of conditions and the following disclaimer.
010     *    * Redistributions in binary form must reproduce the above copyright
011     *      notice, this list of conditions and the following disclaimer in the
012     *      documentation and/or other materials provided with the distribution.
013     *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014     *      names of its contributors may be used to endorse or promote products
015     *      derived from this software without specific prior written permission.
016     * 
017     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025     * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026     * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028     *
029     * This software is Open Source Initiative approved Open Source Software.
030     * Open Source Initative Approved is a trademark of the Open Source Initiative.
031     * 
032     * This file is part of DrJava.  Download the current version of this project
033     * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034     * 
035     * END_COPYRIGHT_BLOCK*/
036    
037    package edu.rice.cs.javalanglevels;
038    
039    import edu.rice.cs.javalanglevels.tree.*;
040    import edu.rice.cs.javalanglevels.parser.*;
041    import edu.rice.cs.javalanglevels.util.*;
042    import java.util.*;
043    import junit.framework.TestCase;
044    
045    /** Represents the data for a given variable (including fields). */
046    public class VariableData {
047      
048      /**The name of this variable*/
049      private String _name;
050      
051      /** The modifiers and visibility. */
052      private ModifiersAndVisibility _modifiersAndVisibility;
053      
054      /** The type of this variable represented by a SymbolData. */
055      private SymbolData _type;
056      
057      /** True if this variable has been given a value*/
058      private boolean _hasBeenAssigned;
059      
060      /** True if this variable has an initializer*/
061      private boolean _hasInitializer;
062      
063      /** The data that this variable belongs to.  For a formal parameter, it is the enclosing class rather than the
064        * enclosing method. */
065      private Data _enclosingData;
066      
067      /** True iff this is a field we had generated. */
068      private boolean _generated;
069      
070      /** True iff this is a method parameter. */
071      private boolean _isLocalVariable;
072      
073      /** An instance corresponding to this; generated on demand. */
074      private InstanceData _instance;
075      
076      /** Constructor for VariableData.  hasInitializer and generated are set to {@code false}.
077        * @param name  The name of the variable
078        * @param modifiersAndVisibility  The modifiersAndVisibility
079        * @param type  The SymbolData type of the variable.
080        * @param hasBeenAssigned  true if this variable has a value
081        * @param enclosingData  the enclosing data
082        */
083      public VariableData(String name, ModifiersAndVisibility modifiersAndVisibility, SymbolData type, 
084                          boolean hasBeenAssigned, Data enclosingData) {
085        _name = name;
086        _modifiersAndVisibility = modifiersAndVisibility;
087        _type = type;
088        _hasBeenAssigned = hasBeenAssigned;
089        _enclosingData = enclosingData;
090        _hasInitializer = false;
091        _generated = false;
092        _isLocalVariable = false;
093        _instance = null;
094      }
095      
096      /** This constructor is only used when reading method parameters in a class file because class files only store the
097        * types of method parameters.
098        */
099      public VariableData(SymbolData type) {
100        _name = "";
101        _modifiersAndVisibility = new ModifiersAndVisibility(SourceInfo.NONE, new String[0]);
102        _type = type;
103        _hasBeenAssigned = false;
104        _isLocalVariable = true;
105        _instance = null;
106      }
107      
108      /** Make a copy of this VariableData erasing all visibility modifiers and setting hasBeenAssigned to true. Used in 
109        * LanguageLevelVisitor.createConstructor to generate the parameter list of the created constructor. 
110        * @return a new Variable Data identical to this except for erasing visibility modifiers. */
111      public VariableData copyWithoutVisibility() {
112        String[] mavStrings = _modifiersAndVisibility.getModifiers();
113        LinkedList<String> newMavList = new LinkedList<String>();
114        int size = 0;
115        for (int i = 0; i < mavStrings.length; i++) {
116          String mod = mavStrings[i];
117          if (! isVisibility(mod)) { 
118            newMavList.add(mod);
119            size++;
120          }
121        }
122        String[] newMavStrings = newMavList.toArray(new String[size]);
123        ModifiersAndVisibility newMav = new ModifiersAndVisibility(SourceInfo.NONE, newMavStrings);
124        return new VariableData(_name, newMav, _type, true, _enclosingData);
125      }
126      
127      /** Checks the values of the fields*/
128      public boolean equals(Object obj) { 
129        if (obj == null) return false;
130        else if (obj.getClass() != this.getClass()) { 
131    //      System.err.println("VariableData.equals: Class equality failure");
132          return false; 
133        }
134        
135        VariableData vd = (VariableData) obj;
136        
137        if (! _name.equals(vd.getName())) {
138    //      System.err.println("VariableData.equals: name equality failure");
139          return false;
140        }
141        if (! _modifiersAndVisibility.equals(vd.getMav())) {
142    //      System.err.println("VariableData.equals: modifiersAndVisibility equality failure");
143          return false;
144        }
145        
146        if (! _type.equals(vd.getType())) {
147    //      System.err.println("VariableData.equals: type equality failure");
148          return false;
149        }
150        
151        if (_hasBeenAssigned != vd.hasValue()) {
152    //      System.err.println("VariableData.equals: hasBeenAssigned equality failure");
153          return false;
154        }
155        if (_hasInitializer != vd._hasInitializer) {
156    //      System.err.println("VariableData.equals: hasInitializer equality failure");
157          return false;
158        }
159        Data otherEnclosingData = vd.getEnclosingData();
160        
161        if (_enclosingData == null) {
162          if (otherEnclosingData == null) return true;
163          else {
164    //      System.err.println("VariableData.equals: enclosingData failure");
165            return false; 
166          }
167        }  // formerly .equals but led to infinite loop when _enclosingData is a VariableData
168        else if (_enclosingData != otherEnclosingData) {  
169    //      System.err.println("VariableData.equals: enclosingData failure");
170          return false; 
171        }
172        
173        return true;
174      }
175      
176      /**Hash on the name and enclosing data, since within each enclosing data, the variable name should be unique*/
177      public int hashCode() { return getEnclosingData().hashCode() ^ getName().hashCode(); }
178      
179      /**@return a readable representation of the Variable data*/
180      public String toString() {
181        return "VariableData(" + _name + ", " + Arrays.toString(_modifiersAndVisibility.getModifiers()) + ", " + _type + 
182          ", " + _hasBeenAssigned + ")";
183      }
184      
185      /**@return the name of this field or variable*/
186      public String getName() { return _name; }
187      
188      /**Set the name of this variable to the specified string. */
189      public void setName(String s) { _name = s; }
190      
191      /**@return the modifiers and visibility. */
192      public ModifiersAndVisibility getMav() { return _modifiersAndVisibility; }
193      
194      /**Set the modifiers and visibility to the specified value. */
195      public void setMav(ModifiersAndVisibility mav) { _modifiersAndVisibility = mav; }
196      
197      /** @return the SymbolData representing the type of this variable. */
198      public SymbolData getType() { return _type;
199        /* The following appears to break the Augmentor visitor.  Why? */
200    //    if (_type != null) return _type; 
201    //    return SymbolData.NOT_FOUND;
202      }
203      
204      /** Sets the SymbolData representing the type of this variable. */
205      public void setType(SymbolData type) { _type = type; }
206      
207      /** Assumes  _type != null
208        * @return the InstanceData corresponding to the type of this variable. */
209      public InstanceData getInstanceData() { 
210        assert _type != null;
211        if (_instance == null) _instance = _type.getInstanceData();
212        return _instance;
213      }
214      
215      /** @return the enclosing data. */
216      public Data getEnclosingData() { return _enclosingData; }
217      
218      /** Sets the enclosing data to the specified value.     */  
219      public void setEnclosingData(Data d) { _enclosingData = d; }
220      
221      /** @return the _isLocalVariable flag.*/  
222      public boolean isLocalVariable() { return _isLocalVariable; }
223      
224      /** Sets the isLocalVariable flag to the specified value.     */  
225      public void setIsLocalVariable(boolean b) { _isLocalVariable = b; }
226      
227      /** Adds "final" to the modifiers and visibility for this class, if it is not already there. */
228      public void setFinal() {
229        String[] newModifiers = Utilities.catenate(_modifiersAndVisibility.getModifiers(), new String[]{"final"});
230    //    System.err.println("modifiers with 'final' = " + Arrays.toString(newModifiers));
231        _modifiersAndVisibility = new ModifiersAndVisibility(SourceInfo.NONE, newModifiers);
232      }
233      
234      /** Adds "private" to the modifiers and visibility for this class, if it is not already there. */
235      public void setPrivate() {
236        if (! hasModifier("private")) {
237          String[] modifiers = _modifiersAndVisibility.getModifiers();
238          String[] newModifiers = new String[modifiers.length + 1];
239          newModifiers[0] = "private";
240          for (int i = 1; i <= modifiers.length; i++) {
241            newModifiers[i] = modifiers[i-1];
242          }
243          _modifiersAndVisibility = new ModifiersAndVisibility(SourceInfo.NONE, newModifiers);
244        }
245      }
246      
247      /** Add the specified modifier to the modifiers and visibility for this class, if it is not already there. */
248      public void addModifier(String s) {
249        if (! hasModifier(s)) {
250          String[] modifiers = _modifiersAndVisibility.getModifiers();
251          String[] newModifiers = new String[modifiers.length + 1];
252          newModifiers[0] = s;
253          for (int i = 1; i <= modifiers.length; i++) {
254            newModifiers[i] = modifiers[i-1];
255          }
256          _modifiersAndVisibility = new ModifiersAndVisibility(SourceInfo.NONE, newModifiers);
257        }
258      }
259      
260      /** Adds "private" and "final" to the modifiers and visibility for this class, if it is not already there. */
261      public void setPrivateAndFinal() {
262        setPrivate();
263        setFinal();
264      }
265      
266      /** Adds "final" and "static" to the modifiers and visibility for this class, if it is not already there. */
267      public void setFinalAndStatic() {
268        setFinal();
269        addModifier("static");
270      }
271      
272      /** Returns true if this VariableData is final. */
273      public boolean isFinal() { return hasModifier("final"); }
274      
275      /** Returns true if this VariableData is private. */
276      public boolean isPrivate() { return hasModifier("private"); }
277      
278      /** Returns true if this VariableData is static. */
279      public boolean isStatic() { return hasModifier("static"); }
280      
281      public static boolean isVisibility(String s) {
282        return s.equals("private") || s.equals("protected") || s.equals("public");
283      }
284      
285      /** Returns true if this variable has the modifier specified. */
286      public boolean hasModifier(String modifier) {
287        String[] mavStrings = _modifiersAndVisibility.getModifiers();
288        for (int i = 0; i < mavStrings.length; i++) {
289          if (mavStrings[i].equals(modifier)) {
290            return true;
291          }
292        }
293        return false;
294      }
295      
296      /**Set the generated flag to the specified value*/
297      public void setGenerated(boolean value) { _generated = value; }
298      
299      /**@return the generated flag*/
300      public boolean isGenerated() { return _generated;  }
301      
302      /** Returns true iff this variable was declared with an initial value */
303      public boolean hasInitializer() { return _hasInitializer; }
304      
305      /** Set the "hasInitializer" flag */
306      public void setHasInitializer(boolean value) { _hasInitializer = value; }
307      
308      /** Returns true if this VariableData has been given a value. */
309      public boolean hasValue() { return _hasBeenAssigned; }
310      
311      /** Set _hasBeenAssigned flag. */
312      public void setHasValue() { _hasBeenAssigned = true; }
313      
314      /** If this VariableData has not been given a value, set _hasBeenAssigned to true, and return true.  Otherwise, 
315        * return false to indicate it already has a value and should not be reassigned. */
316      public boolean gotValue() {
317        if (hasValue()) { return false; }
318        _hasBeenAssigned = true;
319        return true;
320      }
321      
322      /** If this VariableData has a value, set _hasBeenAssigned to false, and return true.  Otherwise, return false. */
323      public boolean lostValue() {
324        if (hasValue()) {
325          _hasBeenAssigned = false;
326          return true;
327        }
328        return false;
329      }
330      
331      /** Test the methods defined in the above class. */
332      public static class VariableDataTest extends TestCase {
333        
334        private VariableData _vd;
335        private VariableData _vd2;
336        
337        private ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
338        private ModifiersAndVisibility 
339          _publicMav2 = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
340        private ModifiersAndVisibility 
341          _protectedMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"protected"});    
342        public VariableDataTest() { this(""); }
343        public VariableDataTest(String name) { super(name); }
344        
345        public void testEquals() {
346          _vd = new VariableData("v", _publicMav, SymbolData.INT_TYPE, true, null);
347          
348          //check variables that are equal;
349          _vd2 = new VariableData("v", _publicMav2, SymbolData.INT_TYPE, true, null);
350          assertTrue("Equals should return true if two VariableDatas are equal", _vd.equals(_vd2));
351          assertTrue("Equals should return true in opposite direction as well", _vd2.equals(_vd));
352          
353          //comparison to null
354          _vd2 = null;
355          assertFalse("Equals should return false if VariableData is compared to null",_vd.equals(null));   
356          
357          //different names
358          _vd2 = new VariableData("q", _publicMav, SymbolData.INT_TYPE, true, null);
359          assertFalse("Equals should return false if variable names are different", _vd.equals(_vd2));
360          
361          //different MAV
362          _vd2 = new VariableData("v", _protectedMav, SymbolData.INT_TYPE, true, null);
363          assertFalse("Equals should return false if variable modifiers are different", _vd.equals(_vd2));
364          
365          //different types
366          _vd2 = new VariableData("v", _publicMav, SymbolData.BOOLEAN_TYPE, true, null);
367          assertFalse("Equals should return false if variable types are different", _vd.equals(_vd2));
368        }
369      }
370    }