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.parser.*;
040    import edu.rice.cs.javalanglevels.tree.*;
041    import edu.rice.cs.javalanglevels.util.Log;
042    import edu.rice.cs.javalanglevels.util.Utilities;
043    import java.io.*;
044    import java.util.*;
045    import junit.framework.TestCase;
046    import edu.rice.cs.plt.reflect.JavaVersion;
047    import edu.rice.cs.plt.iter.*;
048    
049    import static edu.rice.cs.javalanglevels.SourceInfo.NONE;
050    
051    public class Augmentor extends JExpressionIFDepthFirstVisitor<Void> {
052    //  public static final Log _log = new Log("Augmentor.txt", true);
053      
054      private static final String newLine = System.getProperty("line.separator");
055      private static final int indentWidth = 2; // TODO: get this from DrJava?
056    
057      
058      /** IDIOCY Pointer:  Why are all these private fields static?  They are set in the constructor.  Was this design 
059        * choice made to ensure only one Augmentor exists at a time by clobbering fields.  Stupid, stupid, stupid. */
060      
061      /** The original source file to be augmented. */
062      static private BufferedReader _fileIn;
063      
064      /** The current line number in _fileIn. */
065      static private int _fileInLine;
066      
067      /** The current column number in _fileIn.  This is the 1 greater than the last column read. */
068      static private int _fileInColumn;
069      
070      /** The destination file. */
071      static private BufferedWriter _fileOut;
072      
073      /** The current line number in _fileOut. */
074      static private int _fileOutLine;
075      
076      /** The dj* line number to which the current line number in _fileOut corresponds. */
077      static private int _fileOutCorrespondingLine;
078    
079      /** A map from original dj* line number to generated java line number. */
080      static private TreeMap<Integer,Integer> _lineNumberMap;
081      
082      /** The symbol information from this source tree. */
083      static private LanguageLevelVisitor _llv;
084      
085      /** If true, generated toString, hashCode, & equals methods should correctly handle arrays & infinitely recursive
086        * structures */
087      static private boolean _safeSupportCode;
088      
089      /** A String of variable definitions to be written at the end of the top-level class definitions */
090      static private List<String> _endOfClassVarDefs;
091      
092      /** The SymbolData enclosing whatever we are currently augmenting.*/
093      private SymbolData _enclosingData;
094      
095      /** Main constructor for Augmentor: Used by the LanguageLevelConverter when converting language level files.
096        * @param safeSupportCode  true if the user wants safe support code to be generated (this comes with a high overhead)
097        * @param fileIn  A BufferedReader corresponding to the LanguageLevel file we should read from
098        * @param fileOut  A BufferedWriter corresponding to the .java file we should write to.
099        * @param llv  The LanguageLevelVisitor that was used to traverse the language level file.
100        */
101      public Augmentor(boolean safeSupportCode, BufferedReader fileIn, BufferedWriter fileOut, LanguageLevelVisitor llv) {
102        _fileIn = fileIn;
103        _fileInLine = 1;
104        _fileInColumn = 1;
105        _fileOut = fileOut;
106        _fileOutLine = 1;
107        _fileOutCorrespondingLine = 1;
108        _lineNumberMap = new TreeMap<Integer,Integer>();
109        _llv = llv;
110        _safeSupportCode = safeSupportCode;
111        _endOfClassVarDefs = new LinkedList<String>();
112        
113        _enclosingData = null;
114      }
115      
116      /** Create another Augmentor sharing the same static fields as the current Augmentor, but with a new _enclosingData d.
117        * This constructor should only be called from within another Augmentor.
118        * @param d  The EnclosingData from which this Augmentor works.
119        */
120      protected Augmentor(SymbolData d) { _enclosingData = d; }
121      
122      /** This method is called by default from cases that do not override forCASEOnly. */
123      protected Void defaultCase(JExpressionIF that) { return null; } 
124      
125      /** Return a Void array of the specified size. */
126      protected Void[] makeArrayOfRetType(int len) { return new Void[len]; }
127      
128      /** Writes out implicit variableDeclarationModfiers that must be added to augmented file.  If no visibility modifier
129        * is present, this method makes static fields "public final" and instance fields "private final".  If a visibility
130        * modifier is present, it overrides this default.  But all fields are forced to be "final".
131        * @param that is the field declaration being augmented
132        */
133      protected void augmentVariableDeclarationModifiers(VariableDeclaration that) {
134        
135        // make static fields public final, make instance fields private final
136        StringBuilder modifierString = new StringBuilder();
137        String[] modifiers = that.getMav().getModifiers();
138        if (! Utilities.hasVisibilityModifier(modifiers)) {
139          if (Utilities.isStatic(modifiers)) modifierString.append("public ");
140          else modifierString.append("private ");
141        }                                
142        if (! Utilities.isFinal(modifiers)) modifierString.append("final "); 
143        _writeToFileOut(modifierString.toString());
144      }
145      
146      /** Do the augmenting appropriate for a Variable Declaration: all Variable Declarations should
147        * be augmented with "final" if such a modifier is not already present.
148        * @param that  The VariableDeclaration we are augmenting.
149        */
150      public Void forVariableDeclaration(VariableDeclaration that) {
151        _readAndWriteThroughIndex(that.getSourceInfo().getStartLine(), that.getSourceInfo().getStartColumn() - 1);
152        augmentVariableDeclarationModifiers(that);
153        super.forVariableDeclaration(that);
154        return null;
155      }
156      
157      /** All formal parameters (parameters to a method or in a catch clause) are augmented to be "final".
158        * Always read up to the start of the FormalParameter before beginning augmentation.
159        * @param that  The FormalParameter we are augmenting.
160        */
161      public Void forFormalParameter(FormalParameter that) {
162        _readAndWriteThroughIndex(that.getSourceInfo().getStartLine(), that.getSourceInfo().getStartColumn() - 1);
163        if (! that.isIsFinal()) _writeToFileOut("final ");
164          
165        // We don't bother to visit the declarator, since it does not need to be augmented.
166        return null;
167      }
168      
169      /** Do the augmentation necessary for a ConstructorDef.  If no visibility modifier is present, augment with "public"
170        * by default.  The Formal Parameters to the MethodDef need to be visited so that they can be augmented with "final".   
171        * Always read up to the start of the ConstructorDef before beginning augmentation.
172        * @param that  The ConstructorDef we are augmenting.
173        */
174      public Void forConstructorDef(ConstructorDef that) {
175        _readAndWriteThroughIndex(that.getSourceInfo().getStartLine(), that.getSourceInfo().getStartColumn() - 1);
176        
177        // Check and see if the constructor has modifiers.  If not, make it public by default
178        String[] modifiers = that.getMav().getModifiers();
179        if (! Utilities.hasVisibilityModifier(modifiers)) _writeToFileOut("public ");
180    
181        for (FormalParameter fp : that.getParameters()) { fp.visit(this); }
182        // We don't bother visiting the rest of the method declaration
183        return null;
184      }
185      
186      /** Do the augmentation necessary for a MethodDef.  If the user did not specify a visibility level,
187        * the method is automatically augmented to be "public".  Otherwise, the user's modifier is left unchanged.
188        * At all levels, the Formal Parameters to the MethodDef need to be visited so that
189        * they can be augmented with "final" if "final" is not already present. 
190        * Always read up to the start of the MethodDef before beginning augmentation.
191        * @param that  The MethodDef being visited.
192        */
193      public Void forMethodDef(MethodDef that) {
194        SourceInfo mdSourceInfo = that.getSourceInfo();
195        _readAndWriteThroughIndex(mdSourceInfo.getStartLine(), mdSourceInfo.getStartColumn() - 1);
196          
197        if (! Utilities.hasVisibilityModifier(that.getMav().getModifiers())) _writeToFileOut("public ");
198        
199        for (FormalParameter fp : that.getParams()) { fp.visit(this); }
200        // We don't bother visiting the rest of the method declaration
201        return null;
202      }
203      
204      
205      /** Delegate the augmentation of this AbstractMethodDef to forMethodDef.
206        * @param that  The AbstractMethodDef being augmented.
207        */
208      public Void forAbstractMethodDef(AbstractMethodDef md) { forMethodDef(md); return null; }
209      
210      /** Delegate the augmentation of this method def's declaration to forMethodDef.  Then, visit the body with a 
211        * MethodBodyAugmentor so that each piece of the body can be correctly augmented.
212        * @param that  The ConcreteMethodDef being augmented.
213        */
214      public Void forConcreteMethodDef(ConcreteMethodDef that) {
215        assert _enclosingData != null;
216        
217        forMethodDef(that);
218        SymbolData enclosing = _enclosingData.getSymbolData();
219        MethodData md = 
220          enclosing.getMethod(that.getName().getText(), 
221                              formalParameters2TypeDatas(that.getParams(), enclosing));
222    //    _log.log("Augmenting ConcreteMethodDef " + that + " with MethodData " + md);
223        if (md == null) { 
224          throw new RuntimeException("Internal Program Error: Can't find method data for " + that.getName() + 
225                                     " Please report this bug."); 
226        }
227        that.getBody().visit(new MethodBodyAugmentor(enclosing));
228        return null;
229      }
230      
231      /** Class Defs can only appear at the top level of a source file.  If the class type is public but "public" does
232        * not appear as a visibility modifier, add it.  Visit the body of the class definition with a new Augmentor.  
233        * Then, (so that this appears after the rest of the class body) add any necessary augmented methods.
234        * @param cd  The ClassDef we're augmenting.
235        */
236      public Void forClassDef(ClassDef cd) {
237        String className = cd.getName().getText();
238        SymbolData sd = _llv.symbolTable.get(_llv.getQualifiedClassName(className));
239        if (sd == null) { throw new RuntimeException("Internal Program Error: Can't find SymbolData for " + 
240                                                     cd.getName().getText() + " Please report this bug."); }
241        
242        ModifiersAndVisibility m = cd.getMav();
243        String[] modifiers = m.getModifiers();
244        
245        /* Support legacy Elementary level (dj0) files by generating a Junit import statement if this import has
246         * been auto-generated. */
247    
248        if (sd.hasAutoGeneratedJunitImport()) {
249          // import statement should be OK here because language level test files can only contain one class. */
250          _writeToFileOut("import junit.framework.TestCase;" + newLine);  
251        }
252        
253        /* Support legacy Elementary level (dj0) files by add "public " prefix to classes that must be public (TestCase 
254         * files). */
255        if (sd.hasModifier("public") && (! Utilities.isPublic(m.getModifiers()))) {
256          assert ! Utilities.hasVisibilityModifier(modifiers);
257          _readAndWriteThroughIndex(m.getSourceInfo().getStartLine(), m.getSourceInfo().getStartColumn() - 1);
258          _writeToFileOut("public ");
259        }
260        
261        BracedBody bb = cd.getBody();
262        sd.setAnonymousInnerClassNum(0);
263        bb.visit(new Augmentor(sd));
264        
265        int baseIndent = cd.getSourceInfo().getStartColumn() - 1;
266        className = LanguageLevelVisitor.getUnqualifiedClassName(sd.getName());
267        _readAndWriteThroughIndex(cd.getSourceInfo().getEndLine(), cd.getSourceInfo().getEndColumn() - 1);
268        
269        // Augment this class declaration
270        writeConstructor(className, sd, baseIndent);
271        writeAccessors(sd, baseIndent);
272        String valueToStringName = writeValueToString(sd, baseIndent);
273        String valueEqualsName = writeValueEquals(sd, baseIndent);
274        String valueHashCodeName = writeValueHashCode(sd, baseIndent, valueEqualsName);
275        writeToString(sd, baseIndent, valueToStringName);
276        writeEquals(className, sd, baseIndent, valueEqualsName);
277        writeHashCode(className, sd, baseIndent, false, valueHashCodeName);
278        for (String s : _endOfClassVarDefs) {
279          _writeToFileOut(newLine + indentString(baseIndent, 1) + s);
280        }
281        if (_endOfClassVarDefs.size() > 0) {
282          _writeToFileOut(newLine);
283          _endOfClassVarDefs.clear();
284        }
285        _writeToFileOut(indentString(baseIndent, 0));
286        
287        // We don't bother visiting any of the signature nodes -- parameters, type, name, etc.
288        return null;
289      }
290    
291      /** Look up this inner class in the enclosing data, and then visits its body. InnerClassDefs can appear inside method
292        * or class or interface bodies.  No augmentation is done, because an InnerClass can only appear at the 
293        * AdvancedLevel,
294        * and we do not do augmentation at the Advanced Level.  If this were to change, we would need to
295        * add the Augmentation back in.
296        * @param cd  The InnerClassDef we are augmenting.
297        */
298      public Void forInnerClassDef(InnerClassDef cd) {
299        String className = cd.getName().getText();
300        if (_enclosingData == null) {
301          throw new RuntimeException("Internal Program Error: Enclosing Data is null.  Please report this bug.");
302        }
303        SymbolData sd = _enclosingData.getInnerClassOrInterface(className);
304        if (sd == null) {
305          throw new RuntimeException("Internal Program Error: Can't find SymbolData for " + cd.getName().getText() + 
306                                     ". Please report this bug.");
307        }
308        
309        /** WARNING: the code suffix copied from ClassDef; it it works it should be refactored. */
310        BracedBody bb = cd.getBody();
311        sd.setAnonymousInnerClassNum(0);
312        bb.visit(new Augmentor(sd));
313        
314        int baseIndent = cd.getSourceInfo().getStartColumn() - 1;
315        className = LanguageLevelVisitor.getUnqualifiedClassName(sd.getName());
316        _readAndWriteThroughIndex(cd.getSourceInfo().getEndLine(), cd.getSourceInfo().getEndColumn() - 1);
317        
318        // Augment this class declaration
319        writeConstructor(className, sd, baseIndent);
320        writeAccessors(sd, baseIndent);
321        String valueToStringName = writeValueToString(sd, baseIndent);
322        String valueEqualsName = writeValueEquals(sd, baseIndent);
323        String valueHashCodeName = writeValueHashCode(sd, baseIndent, valueEqualsName);
324        writeToString(sd, baseIndent, valueToStringName);
325        writeEquals(className, sd, baseIndent, valueEqualsName);
326        writeHashCode(className, sd, baseIndent, false, valueHashCodeName);
327        for (String s : _endOfClassVarDefs) {
328          _writeToFileOut(newLine + indentString(baseIndent, 1) + s);
329        }
330        if (_endOfClassVarDefs.size() > 0) {
331          _writeToFileOut(newLine);
332          _endOfClassVarDefs.clear();
333        }
334        _writeToFileOut(indentString(baseIndent, 0));
335        
336        
337    // We don't bother visiting any of the signature nodes -- parameters, type, name, etc.
338        return null;
339      }
340      
341      /* Old suffix */
342      
343    //    BracedBody bb = cd.getBody();
344    //    sd.setAnonymousInnerClassNum(0);
345    //    bb.visit(new Augmentor(sd));
346    //
347    //    _readAndWriteThroughIndex(cd.getSourceInfo().getEndLine(), cd.getSourceInfo().getEndColumn() - 1);;
348    //
349    //    // We don't bother visiting any of the signature nodes -- parameters, type, name, etc.
350    //    return null;
351    //  }
352      
353      /** Look up this top level interface in the symbolTable, and then visit its body.  Write any necessary extra variable
354        * definitions at the end of the file.  InterfaceDefs can only appear at the top level of a source file.
355        * @param cd  The InterfaceDef being augmented.
356        */
357      public Void forInterfaceDef(InterfaceDef cd) {
358        String interfaceName = cd.getName().getText();
359        SymbolData sd = _llv.symbolTable.get(_llv.getQualifiedClassName(interfaceName));
360        if (sd == null) { throw new RuntimeException("Internal Program Error: Can't find SymbolData for " +
361                                                     cd.getName().getText() + ".  Please report this bug."); }
362        ModifiersAndVisibility m = cd.getMav();
363        
364        /* Make interfaces public by default? */
365    //    if (! Utilities.hasVisibilityModifier(m.getModifiers()) { 
366    //      _readAndWriteThroughIndex(m.getSourceInfo().getStartLine(), m.getSourceInfo().getStartColumn() - 1);
367    //      _writeToFileOut("public ");
368    //    }
369    
370        BracedBody bb = cd.getBody();
371        sd.setAnonymousInnerClassNum(0);
372        bb.visit(new Augmentor(sd));
373        
374        int baseIndent = cd.getSourceInfo().getStartColumn() - 1;
375        _readAndWriteThroughIndex(cd.getSourceInfo().getEndLine(), cd.getSourceInfo().getEndColumn() - 1);
376        for (String s : _endOfClassVarDefs) {
377          _writeToFileOut(newLine + indentString(baseIndent, 1) + s);
378        }
379        if (_endOfClassVarDefs.size() > 0) {
380          _writeToFileOut(newLine);
381          _endOfClassVarDefs.clear();
382        }
383        _writeToFileOut(indentString(baseIndent, 0));
384        // We don't bother visiting any of the signature nodes -- parameters, type, name, etc.
385        return null;
386      }
387    
388      /** Look up this inner interface in the enclosing data, and then visit its body with a new Augmentor.
389        * No code augmentation is done since InnerInterfaces can only appear at the Advanced Level, and
390        * we are not doing code augmentation at the Advanced Level.  If we were to start doing code augmentation 
391        * at the Advanced level, this might need to change.
392        * InnerInterfaceDefs can appear inside method or class or interface bodies.
393        */
394      public Void forInnerInterfaceDef(InnerInterfaceDef cd) {
395        String interfaceName = cd.getName().getText();
396        if (_enclosingData == null) {
397          throw new RuntimeException("Internal Program Error: Enclosing Data is null.  Please report this bug.");
398        }
399        SymbolData sd = _enclosingData.getInnerClassOrInterface(interfaceName);
400        if (sd == null) { 
401          throw new RuntimeException("Internal Program Error: Can't find SymbolData for " + cd.getName().getText() + 
402                                     ". Please report this bug."); 
403        }
404    
405        BracedBody bb = cd.getBody();
406        sd.setAnonymousInnerClassNum(0);
407        bb.visit(new Augmentor(sd));
408        
409        // We don't bother visiting any of the signature nodes -- parameters, type, name, etc.
410        return null;
411      }
412      
413      
414      /* Look up this Anonymous Inner Class inside its enclosing data, and then visit its body.  Then, if it
415       * is from an Elementary or Intermediate Level file, augment with the necessary automatically generated methods.
416       * @param e  The AnonymousClassInstantiation we are augmenting.
417       */
418      public Void forAnonymousClassInstantiation(AnonymousClassInstantiation e) {
419        SymbolData sd = _enclosingData.getNextAnonymousInnerClass();
420    //    _log.log("Augmenting anonymous class " + e + " with SymbolData " + sd);
421        if (sd == null) {
422          throw new RuntimeException("Internal Program Error: Couldn't find the SymbolData for the anonymous inner class." +
423                                     "  Please report this bug.");
424        }
425        BracedBody bb = e.getBody();
426        sd.setAnonymousInnerClassNum(0);
427        bb.visit(new Augmentor(sd));
428        
429        int baseIndent = e.getSourceInfo().getStartColumn() - 1;
430        _readAndWriteThroughIndex(e.getSourceInfo().getEndLine(), e.getSourceInfo().getEndColumn() - 1);
431    //    if (_isElementaryFile() || _isIntermediateFile()) {
432        String className = Data.dollarSignsToDots(e.getType().getName());
433        writeAccessors(sd, baseIndent);
434        String valueToStringName = writeValueToString(sd, baseIndent);
435        String valueEqualsName = writeValueEquals(sd, baseIndent);
436        String valueHashCodeName = writeValueHashCode(sd, baseIndent, valueEqualsName);
437        writeToString(sd, baseIndent, valueToStringName);
438        if (!_safeSupportCode) { writeAnonEquals(baseIndent);}
439        else { writeEquals(className, sd, baseIndent, valueEqualsName); }
440        writeHashCode(className, sd, baseIndent, true, valueHashCodeName);
441        _writeToFileOut(indentString(baseIndent, 0));
442        
443    //    }
444        return null;
445      }
446      
447      /**Delegate to for AnonymousClassInstantiation(e).*/
448      public Void forSimpleAnonymousClassInstantiation(SimpleAnonymousClassInstantiation e) {
449        forAnonymousClassInstantiation(e);
450        return null;
451      }
452      
453      /** Visit the encosing part of this ComplexAnonymousClass name, and then delegate to 
454        * forAnonymousClassInstantiation(e). */
455      public Void forComplexAnonymousClassInstantiation(ComplexAnonymousClassInstantiation e) {
456        e.getEnclosing().visit(this);
457        forAnonymousClassInstantiation(e);
458        return null;
459      }
460    
461      /** Sort the Class and Interface defs based on the order they appear in the file.  Then visit each in turn.
462        * Finally, write whatever remains in the file.  (If this is an ElementaryLevel file that needs to import
463        * junit.framework.TestCase, make this the very first line of the augmented file.  This is okay, because at the
464        * ElementaryLevel, there are no package statements we might get in trouble with.
465        */
466      public Void forSourceFile(SourceFile sf) {
467        TypeDefBase[] cds = sf.getTypes();
468        
469        // We intentionally neglect to visit the package and import statements
470    
471        //Visit each class and interface def in turn.
472        for (TypeDefBase cd : cds) { cd.visit(this); }
473     
474        //Write out whatever is left in the file.
475    //    _log.log("Processed source file through line " + _fileInLine + " and column " + _fileInColumn);
476    //    _log.log("Now dumping remainder of file through line " + sf.getSourceInfo().getEndLine());
477        //_readAndWriteThroughIndex(sf.getSourceInfo().getEndLine(), sf.getSourceInfo().getEndColumn());
478        String remainder = _readThroughIndex(sf.getSourceInfo().getEndLine(), sf.getSourceInfo().getEndColumn());
479        if (!remainder.endsWith(newLine)) remainder = remainder + newLine; // make sure file ends in a newLine
480        _writeToFileOut(remainder, true);
481        return null;
482      }
483      
484      /** Convert the provided FormalParameter array into an array of TypeData corresponding
485        * to the types of the FormalParameters.
486        */
487      protected static TypeData[] formalParameters2TypeDatas(FormalParameter[] fps, SymbolData enclosing) { 
488        TypeData[] tds = new TypeData[fps.length];
489        int j = 0;
490        for (FormalParameter fp: fps) {
491          SymbolData type = _llv.getSymbolData(fp.getDeclarator().getType().getName(), fp.getSourceInfo());
492          
493          if (type == null) {
494            //see if this is a partially qualified field reference
495            type = enclosing.getInnerClassOrInterface(fp.getDeclarator().getType().getName());
496          }
497          
498          tds[j]= type; j++;  // store type in next empty slot of tds
499        }
500        assert j == fps.length;
501        
502        return tds;
503      }
504      
505      
506      /**
507       * Write a constructor for a previously generated (determined by MethodData.isGenerated())
508       * constructor, if one exists.
509       * 
510       * @param sd  The method's enclosing class.
511       * @param baseIndent  The base indent level (number of spaces).
512       */
513      protected static void writeConstructor(String className, SymbolData sd, int baseIndent) {
514        // Find the constructor.  There should be at most one that we generated, so select that one.
515        MethodData constructor = null;
516        for (MethodData currMd : sd.getMethods()) {
517          if (currMd.getName().equals(LanguageLevelVisitor.getUnqualifiedClassName(sd.getName())) && currMd.isGenerated()) {
518            constructor = currMd;
519            break;
520          }
521        }
522        if (constructor == null) return;
523        
524        
525        // Write the method signature.
526        VariableData[] constructorParams = constructor.getParams();
527        
528        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
529                        "/** This method is automatically generated by the Language Level Converter. */" + newLine);
530        _writeToFileOut(indentString(baseIndent, 1) + "public " + className + "(");
531        for (int q = 0; q < constructorParams.length; q++) {
532          if (q>0) {
533            _writeToFileOut(", ");
534          }
535          VariableData vd = constructorParams[q];
536          _writeToFileOut(Data.dollarSignsToDots(vd.getType().getName()) + " " + vd.getName());
537        }
538        
539        _writeToFileOut(") {" + newLine);
540        
541    
542        // Generate lists of super params & local params
543        LinkedList<VariableData> superParams = new LinkedList<VariableData>();
544        LinkedList<VariableData> localParams = new LinkedList<VariableData>();
545        LinkedList<VariableData> localFields = sd.getVars();
546        
547        for (VariableData param : constructorParams) {
548          boolean hasLocalField = false;
549          for (VariableData field : localFields) {
550            hasLocalField = hasLocalField || field.getName().equals(param.getName());
551            if (hasLocalField) { break; }
552          }
553          
554          if (hasLocalField) { localParams.add(param); }
555          else if (param.getName().startsWith("super_")) { superParams.add(param); }
556          else { 
557            throw new RuntimeException("Internal Program Error: Unexpected parameter name in generated constructor: " + 
558                                       param.getName() +".  Please report this bug"); 
559          }
560        }
561        
562        // Add the call to the super constructor here.
563        _writeToFileOut(indentString(baseIndent, 2) + "super(");
564        
565        for (int z = 0; z < superParams.size(); z++) {
566          if (z > 0) {
567            _writeToFileOut(", ");
568          }
569          _writeToFileOut(superParams.get(z).getName());
570        }
571        _writeToFileOut(");" + newLine);
572        
573        // Instantiate local fields.
574        for (int i = 0; i < localParams.size(); i++) {
575          String varName = localParams.get(i).getName();
576          _writeToFileOut(indentString(baseIndent, 2) + "this." + varName + " = " + varName + ";" + newLine);
577        }
578        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);  
579      }
580      
581      /**
582       * Write an accessor method for each previously generated (determined by MethodData.isGenerated())
583       * accessor.
584       * 
585       * @param sd  The method's enclosing class.
586       * @param baseIndent  The base indent level (number of spaces).
587       */
588      protected static void writeAccessors(SymbolData sd, int baseIndent) {
589        // Find the accessor methods and generate them if we had to create them ourselves.
590        LinkedList<MethodData> methods = sd.getMethods();
591        MethodData accessor = null;
592        Iterator<MethodData> iter = methods.iterator();
593        VariableData[] vars = sd.getVars().toArray(new VariableData[sd.getVars().size()]);
594        for (int i = 0; i < vars.length; i++) {
595          accessor = null;
596          iter = methods.iterator();
597          while (iter.hasNext()) {
598            MethodData currMd = iter.next();
599            if (currMd.getName().equals(vars[i].getName()) && currMd.isGenerated()) {
600              accessor = currMd;
601              break;
602            }
603          }
604          if (accessor != null) {
605            _writeToFileOut(newLine + indentString(baseIndent, 1) + 
606                            "/** This method is automatically generated by the Language Level Converter. */" + newLine);
607            _writeToFileOut(indentString(baseIndent, 1) + "public " + 
608                            Data.dollarSignsToDots(vars[i].getType().getName()) + " " + 
609                            LanguageLevelVisitor.getFieldAccessorName(vars[i].getName()) + "() {" + newLine);
610            _writeToFileOut(indentString(baseIndent, 2) + "return " + vars[i].getName() + ";" + newLine + 
611                            indentString(baseIndent, 1) + "}" + newLine);
612          }
613        }
614      }
615      
616      /**
617       * Write a toString method that prints out each field with a visible accessor.
618       * 
619       * @param sd  The method's enclosing class.
620       * @param baseIndent  The base indent level (number of spaces).
621       * @param valueToStringName  The name of the generated valueToString method
622       */
623      protected static void writeToString(SymbolData sd, int baseIndent, String valueToStringName) {
624        LinkedList<MethodData> methods = sd.getMethods();
625        MethodData toString = null;
626        Iterator<MethodData> iter = methods.iterator();
627        // Find the toString method and generate it if we had to create it ourselves.
628        while (iter.hasNext()) {
629          MethodData currMd = iter.next();
630          if (currMd.getName().equals("toString") && currMd.isGenerated()) {
631            toString = currMd;
632            break;
633          }
634        }
635        if (toString == null) return;
636        // Builds a list of MethodData accessors for VariableDatas of this class.
637        LinkedList<MethodData> allMds = _getVariableAccessorListHelper(sd);
638        MethodData[] mds = allMds.toArray(new MethodData[allMds.size()]);
639    
640        if (_safeSupportCode) { writeSafeToString(sd, baseIndent, valueToStringName, mds); }
641        else { writeSimpleToString(sd, baseIndent, valueToStringName, mds); }
642      }
643        
644      /** Helper to writeToString; writes a toString that handles infinitely-recursive data structures. */
645      protected static void writeSafeToString(SymbolData sd, int baseIndent, String valueToStringName, 
646                                              MethodData[] accessors) {
647        
648        String flagName = sd.createUniqueName("__toStringFlag");
649        VariableData toStringFlag = new VariableData(flagName, 
650                                                     new ModifiersAndVisibility(NONE, 
651                                                                                new String[]{ "private", "static" }),
652                                     _llv.getQualifiedSymbolData("java.util.LinkedList", SourceInfo.NONE, false),
653                                     true, sd);
654        toStringFlag.setGenerated(true);
655        sd.addVar(toStringFlag);
656        
657        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
658                        "/** This field is automatically generated by the Language Level Converter. */" + newLine);
659        _writeToFileOut(indentString(baseIndent, 1) + "private boolean " + flagName + " = false;" + newLine);
660        _writeToFileOut(newLine + indentString(baseIndent, 1) +
661                        "/** This method is automatically generated by the Language Level Converter. */" + newLine);
662        _writeToFileOut(indentString(baseIndent, 1) + "public java.lang.String toString() {" + newLine);
663        _writeToFileOut(indentString(baseIndent, 2) + "if (" + flagName + ") {" + newLine);
664        _writeToFileOut(indentString(baseIndent, 3) + "return getClass().getName() + \"(...)\";" + newLine);
665        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
666        _writeToFileOut(indentString(baseIndent, 2) + "else {" + newLine);
667        _writeToFileOut(indentString(baseIndent, 3) + flagName + " = true;" + newLine);
668        _writeToFileOut(indentString(baseIndent, 3) + "String result;" + newLine);
669        _writeToFileOut(indentString(baseIndent, 3) + "try {" + newLine);
670        _writeToFileOut(indentString(baseIndent, 4) + "result = getClass().getName() + \"(\" + " + newLine);
671        for (int i = 0; i < accessors.length; i++) {
672          if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5) ||
673                ! accessors[i].getReturnType().getSymbolData().isPrimitiveType()) {
674            
675            _writeToFileOut(indentString(baseIndent, 6) + valueToStringName + "(" + accessors[i].getName() + "()) + ");
676          }
677          else {
678            _writeToFileOut(indentString(baseIndent, 6) + accessors[i].getName() + "() + ");
679          }
680    
681          if (i < accessors.length - 1) {
682            _writeToFileOut("\", \" + ");
683          }
684          _writeToFileOut(newLine);
685        }
686        _writeToFileOut(indentString(baseIndent, 6) + "\")\";" + newLine);
687        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine);
688        _writeToFileOut(indentString(baseIndent, 3) + "catch (RuntimeException e) {" + newLine);
689        _writeToFileOut(indentString(baseIndent, 4) + flagName + " = false;" + newLine);
690        _writeToFileOut(indentString(baseIndent, 4) + "throw e;" + newLine);
691        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine);
692        _writeToFileOut(indentString(baseIndent, 3) + flagName + " = false;" + newLine);
693        _writeToFileOut(indentString(baseIndent, 3) + "return result;" + newLine);
694        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
695        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
696      }
697      
698      /** Helper to writeToString; writes a short toString that does not handle infinitely-recursive data structures. */
699      protected static void writeSimpleToString(SymbolData sd, int baseIndent, String valueToStringName,
700                                                MethodData[] accessors) {
701        
702        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
703                        "/** This method is automatically generated by the Language Level Converter. */" + newLine);
704        _writeToFileOut(indentString(baseIndent, 1) + "public java.lang.String toString() {" + newLine);
705        _writeToFileOut(indentString(baseIndent, 2) + "return getClass().getName() + \"(\" + " + newLine);
706        for (int i = 0; i < accessors.length; i++) {
707            _writeToFileOut(indentString(baseIndent, 4) + accessors[i].getName() + "() + ");
708    
709          if (i < accessors.length - 1) {
710            _writeToFileOut("\", \" + ");
711          }
712          _writeToFileOut(newLine);
713        }
714        _writeToFileOut(indentString(baseIndent, 4) + "\")\";" + newLine);
715        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
716      }
717      
718      /**
719       * Write an equals method that requires the Object parameter to be an instanceof this type
720       * and have matching fields for each of the fields with visible accessors.
721       * 
722       * @param className  The unqualified name of this method's class or, in the case of anonymous inner classes, the
723       *                   name of its superclass (or implemented interface).
724       * @param sd  The method's enclosing class.
725       * @param baseIndent  The base indent level (number of spaces).
726       * @param valueEqualsName  The name of the generated valueEquals method
727       */
728      protected static void writeEquals(String className, SymbolData sd, int baseIndent, String valueEqualsName) {
729        LinkedList<MethodData> methods = sd.getMethods();
730        MethodData equals = null;
731        Iterator<MethodData> iter = methods.iterator();
732    
733        // Find the equals method and generate it if we had to create it ourselves.
734        while (iter.hasNext()) {
735          MethodData currMd = iter.next();
736          if (currMd.getName().equals("equals") && currMd.isGenerated()) {
737            equals = currMd;
738            break;
739          }
740        }
741        if (equals == null) return;
742    
743        LinkedList<MethodData> allMds = _getVariableAccessorListHelper(sd);
744        MethodData[] mds = allMds.toArray(new MethodData[allMds.size()]);
745    
746        if (_safeSupportCode) { writeSafeEquals(className, sd, baseIndent, valueEqualsName, mds); }
747        else { writeSimpleEquals(className, sd, baseIndent, valueEqualsName, mds); }
748      }
749        
750      /** Helper to writeEquals; writes an equals that handles infinitely-recursive data structures. */
751      protected static void writeSafeEquals(String className, SymbolData sd, int baseIndent, String valueEqualsName, 
752                                            MethodData[] accessors) {
753        
754        String listName = sd.createUniqueName("__equalsList");
755        
756        VariableData equalsList = 
757          new VariableData(listName, new ModifiersAndVisibility(SourceInfo.NONE, new String[]{ "private", "static" }),
758                           _llv.getQualifiedSymbolData("java.util.LinkedList", SourceInfo.NONE, false),
759                           true, sd);
760        equalsList.setGenerated(true);
761        sd.addVar(equalsList);
762        
763        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
764                        "/** This field is automatically generated by the Language Level Converter. */");
765        if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5))
766          _writeToFileOut(newLine + indentString(baseIndent, 1) + "private java.util.LinkedList<" + className + "> " +
767                          listName + " = new java.util.LinkedList<" + className + ">();" + newLine);
768        else
769          _writeToFileOut(newLine + indentString(baseIndent, 1) + "private java.util.LinkedList " + listName + 
770                          " = new java.util.LinkedList();" + newLine + newLine);
771    
772        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
773                        "/** This method is automatically generated by the Language Level Converter. */" + newLine);
774        _writeToFileOut(indentString(baseIndent, 1) + "public boolean equals(java.lang.Object o) {" + newLine);
775        _writeToFileOut(indentString(baseIndent, 2) + "if (this == o) {" + newLine);
776        _writeToFileOut(indentString(baseIndent, 3) + "return true;" + newLine);
777        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
778        _writeToFileOut(indentString(baseIndent, 2) + "else if ((o == null) || (! o.getClass().equals(getClass()))) {" +
779                        newLine);
780        _writeToFileOut(indentString(baseIndent, 3) + "return false;" + newLine);
781        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
782        _writeToFileOut(indentString(baseIndent, 2) + "else {" + newLine);
783        _writeToFileOut(indentString(baseIndent, 3) + "boolean alreadyTested = false;" + newLine);
784        if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5)) {
785          _writeToFileOut(indentString(baseIndent, 3) + "for (" + className + " element : " + listName + ")" + newLine);
786          _writeToFileOut(indentString(baseIndent, 4) + "alreadyTested = alreadyTested || (o == element);" + newLine + 
787                          newLine);
788        }
789        else {
790          if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5)) {
791            _writeToFileOut(indentString(baseIndent, 3) + "java.util.Iterator<" + className + "> i = " + listName + 
792                            ".iterator();" + newLine);
793          }
794          else {
795            _writeToFileOut(indentString(baseIndent, 3) + "java.util.Iterator i = " + listName + ".iterator();" + newLine);
796          }
797          _writeToFileOut(indentString(baseIndent, 3) + "while (!alreadyTested && i.hasNext())" + newLine);
798          _writeToFileOut(indentString(baseIndent, 4) + "alreadyTested = alreadyTested || (o == i.next());" + newLine +
799                          newLine);
800        }
801        _writeToFileOut(indentString(baseIndent, 3) + "if (alreadyTested) { " + newLine);
802        _writeToFileOut(indentString(baseIndent, 4) + "return true;" + newLine);
803        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine);
804        _writeToFileOut(indentString(baseIndent, 3) + "else {" + newLine);
805        _writeToFileOut(indentString(baseIndent, 4) + className + " cast = ((" + className + ") o);" + newLine);
806        _writeToFileOut(indentString(baseIndent, 4) + listName + ".addLast(cast);" + newLine);
807        _writeToFileOut(indentString(baseIndent, 4) + "boolean result;" + newLine);
808        _writeToFileOut(indentString(baseIndent, 4) + "try {" + newLine);
809        _writeToFileOut(indentString(baseIndent, 5) + "result = ");
810        int variablesCompared = 0;
811        for (int i = 0; i < accessors.length; i++) {
812          if (variablesCompared > 0) {
813            _writeToFileOut(" && " + newLine + indentString(baseIndent, 7));
814          }
815          variablesCompared++;
816          
817          String varName = accessors[i].getName() + "()";
818          
819          if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5) ||
820                ! accessors[i].getReturnType().getSymbolData().isPrimitiveType()) {
821            
822            _writeToFileOut(valueEqualsName + "(" + varName + ", cast." + varName + ")");
823          }
824          else {
825            _writeToFileOut("(" + varName + " == cast." + varName + ")");
826          }
827        }
828      
829        if (variablesCompared == 0)
830          _writeToFileOut("true");
831        
832        _writeToFileOut(";" + newLine);
833        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
834        _writeToFileOut(indentString(baseIndent, 4) + "catch (RuntimeException e) {" + newLine);
835        _writeToFileOut(indentString(baseIndent, 5) + listName + ".removeLast();" + newLine);
836        _writeToFileOut(indentString(baseIndent, 5) + "throw e;" + newLine);
837        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
838        _writeToFileOut(indentString(baseIndent, 4) + listName + ".removeLast();" + newLine);
839        _writeToFileOut(indentString(baseIndent, 4) + "return result;" + newLine);
840        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine);
841        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
842        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
843      }
844      
845      /** Helper to writeEquals; writes a simple equals that does not handle infinitely-recursive data structures. */
846      protected static void writeSimpleEquals(String className, SymbolData sd, int baseIndent, String valueEqualsName, 
847                                              MethodData[] accessors) {
848        
849        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
850                        "/** This method is automatically generated by the Language Level Converter. */" + newLine);
851        _writeToFileOut(indentString(baseIndent, 1) + "public boolean equals(java.lang.Object o) {" + newLine);
852        _writeToFileOut(indentString(baseIndent, 2) + "if (this == o) {" + newLine);
853        _writeToFileOut(indentString(baseIndent, 3) + "return true;" + newLine);
854        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
855        _writeToFileOut(indentString(baseIndent, 2) + "else if ((o == null) || (! o.getClass().equals(getClass()))) {" +
856                        newLine);
857        _writeToFileOut(indentString(baseIndent, 3) + "return false;" + newLine);
858        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
859        _writeToFileOut(indentString(baseIndent, 2) + "else {" + newLine);
860        _writeToFileOut(indentString(baseIndent, 3) + className + " cast = ((" + className + ") o);" + newLine);
861        _writeToFileOut(indentString(baseIndent, 3) + "return ");
862        int variablesCompared = 0;
863        for (int i = 0; i < accessors.length; i++) {
864          if (variablesCompared > 0) {
865            _writeToFileOut(" && " + newLine + indentString(baseIndent, 5));
866          }
867          variablesCompared++;
868          
869          String varName = accessors[i].getName() + "()";
870          
871          if (! accessors[i].getReturnType().getSymbolData().isPrimitiveType()) {
872            
873            _writeToFileOut("(" + varName + " != null && " + varName + ".equals(cast." + varName + "))");
874          }
875          else {
876            _writeToFileOut("(" + varName + " == cast." + varName + ")");
877          }
878        }
879      
880        if (variablesCompared == 0)
881          _writeToFileOut("true");
882        
883        _writeToFileOut(";" + newLine);
884        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
885        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
886      }
887      
888    
889      /** 
890       * AnonymousClasses are only equal if they are identical. 
891       */
892      protected static void writeAnonEquals(int baseIndent) {
893        
894        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
895                        "/** This method is automatically generated by the Language Level Converter. */" + newLine);
896        _writeToFileOut(indentString(baseIndent, 1) + "public boolean equals(java.lang.Object o) {" + newLine);
897        _writeToFileOut(indentString(baseIndent, 2) + "return (this == o);" + newLine);
898        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
899      }
900      
901      
902      /**
903       * Write a hashCode method that is consistent with the generated equals(Object) method.
904       * 
905       * @param className  The unqualified name of this method's class or, in the case of anonymous inner classes, the
906       *                   name of its superclass (or implemented interface).
907       * @param sd  The method's enclosing class.
908       * @param baseIndent  The base indent level (number of spaces).
909       * @param waitForVarDef  True iff static variables cannot be defined in the current context and should be deferred
910       *                       by adding them to _endOfClassVarDefs.
911       * @param valueHashCodeName  The name of the generated valueHashCode method
912       */
913      protected static void writeHashCode(String className, SymbolData sd, int baseIndent, boolean waitForVarDef, 
914                                          String valueHashCodeName) {
915        LinkedList<MethodData> methods = sd.getMethods();
916        MethodData hashCode = null;
917        Iterator<MethodData> iter = methods.iterator();
918    
919        // Find the hashCode method and generate it if we had to create it ourselves.
920        while (iter.hasNext()) {
921          MethodData currMd = iter.next();
922          if (currMd.getName().equals("hashCode") && currMd.isGenerated()) {
923            hashCode = currMd;
924            break;
925          }
926        }
927        if (hashCode == null) return;
928    
929        LinkedList<MethodData> allMds = _getVariableAccessorListHelper(sd);
930        MethodData[] mds = allMds.toArray(new MethodData[allMds.size()]);
931        
932        if (_safeSupportCode) { writeSafeHashCode(className, sd, baseIndent, waitForVarDef, valueHashCodeName, mds); }
933        else { writeSimpleHashCode(className, sd, baseIndent, waitForVarDef, valueHashCodeName, mds); }
934      }
935        
936      /** Helper to writeHashCode; writes a hashCode that handles infinitely-recursive data structures.   
937       * @param className  The unqualified name of this method's class or, in the case of anonymous inner classes, the
938       *                   name of its superclass (or implemented interface).
939       * @param sd  The method's enclosing class.
940       * @param baseIndent  The base indent level (number of spaces).
941       * @param waitForVarDef  True iff static variables cannot be defined in the current context and should be deferred
942       *                       by adding them to _endOfClassVarDefs.
943       * @param valueHashCodeName  The name of the generated valueHashCode method
944       * @param accessors  An Array of the MethodDatas corresponding to the accessors for this class.
945       */
946      protected static void writeSafeHashCode(String className, SymbolData sd, int baseIndent, boolean waitForVarDef, 
947                                              String valueHashCodeName, MethodData[] accessors) {
948        
949        String listName = "__hashCodeList";
950        listName = sd.createUniqueName(listName);
951        VariableData hashCodeList =
952          new VariableData(listName, new ModifiersAndVisibility(SourceInfo.NONE, new String[]{ "private", "static" }),
953                           _llv.getQualifiedSymbolData("java.util.LinkedList", SourceInfo.NONE, false),
954                           true, sd);
955        hashCodeList.setGenerated(true);
956        
957        if (waitForVarDef) {
958          SymbolData outermostData = sd;
959          while (outermostData.getOuterData() != null) {
960            outermostData = outermostData.getOuterData().getSymbolData();
961          }
962          outermostData.addVar(hashCodeList);
963          _endOfClassVarDefs.add("/** This field is automatically generated by the Language Level Converter. */");
964          if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5)) {
965            _endOfClassVarDefs.add("private static java.util.LinkedList<Object> " + listName + 
966                                   " = new java.util.LinkedList<Object>();");
967          }
968          else {
969            _endOfClassVarDefs.add("private static java.util.LinkedList " + listName + " = new java.util.LinkedList();");
970          }
971          _endOfClassVarDefs.add("");
972        }
973        else {
974          sd.addVar(hashCodeList);
975          _writeToFileOut(newLine + indentString(baseIndent, 1) + 
976                          "/** This field is automatically generated by the Language Level Converter. */");
977          if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5)) {
978            _writeToFileOut(newLine + indentString(baseIndent, 1) +
979                            "private static java.util.LinkedList<" + className + "> " + listName + 
980                            " = new java.util.LinkedList<" + className + ">();" + newLine);
981          }
982          else {
983            _writeToFileOut(newLine + indentString(baseIndent, 1) + 
984                            "private static java.util.LinkedList " + listName + " = new java.util.LinkedList();" + newLine);
985          }
986        }
987        
988        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
989                        "/** This method is automatically generated by the Language Level Converter. */");
990        _writeToFileOut(newLine + indentString(baseIndent, 1) + "public int hashCode() {" + newLine);
991        _writeToFileOut(indentString(baseIndent, 2) + "if (" + listName + ".contains(this)) {" + newLine);
992        _writeToFileOut(indentString(baseIndent, 3) + "return -1;" + newLine);
993        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
994        _writeToFileOut(indentString(baseIndent, 2) + "else {" + newLine);
995        _writeToFileOut(indentString(baseIndent, 3) + listName + ".addLast(this);" + newLine);
996        _writeToFileOut(indentString(baseIndent, 3) + "int result;" + newLine);
997        _writeToFileOut(indentString(baseIndent, 3) + "try {" + newLine);
998        _writeToFileOut(indentString(baseIndent, 4) + "result = getClass().hashCode()");
999        for (int i = 0; i < accessors.length; i++) {
1000          _writeToFileOut(" ^ " + newLine + indentString(baseIndent, 6));
1001          SymbolData type = accessors[i].getReturnType().getSymbolData();
1002          
1003          if (LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5) ||
1004              ! type.isPrimitiveType()) {
1005            
1006            _writeToFileOut(valueHashCodeName + "(" + accessors[i].getName() + "())");
1007          }
1008          else if (type == SymbolData.BOOLEAN_TYPE) {
1009            _writeToFileOut("(" + accessors[i].getName() + "() ? 1 : 0)");
1010          }
1011          else if (type.isAssignableTo(SymbolData.INT_TYPE, LanguageLevelConverter.OPT.javaVersion())) {
1012            _writeToFileOut(accessors[i].getName() + "()");
1013          }
1014          else {
1015            _writeToFileOut("(int) " + accessors[i].getName() + "()");           
1016          }
1017        }
1018        
1019        _writeToFileOut(";" + newLine);
1020        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine);
1021        _writeToFileOut(indentString(baseIndent, 3) + "catch (RuntimeException e) {" + newLine);
1022        _writeToFileOut(indentString(baseIndent, 4) + listName + ".removeLast();" + newLine);
1023        _writeToFileOut(indentString(baseIndent, 4) + "throw e;" + newLine);
1024        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine);
1025        _writeToFileOut(indentString(baseIndent, 3) + listName + ".removeLast();" + newLine);
1026        _writeToFileOut(indentString(baseIndent, 3) + "return result;" + newLine);
1027        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine);
1028        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1029      }
1030      
1031      /** Helper to writeHashCode; writes a simple hashCode that does not handle infinitely-recursive data structures. 
1032       * @param className  The unqualified name of this method's class or, in the case of anonymous inner classes, the
1033       *                   name of its superclass (or implemented interface).
1034       * @param sd  The method's enclosing class.
1035       * @param baseIndent  The base indent level (number of spaces).
1036       * @param waitForVarDef  True iff static variables cannot be defined in the current context and should be deferred
1037       *                       by adding them to _endOfClassVarDefs.
1038       * @param valueHashCodeName  The name of the generated valueHashCode method
1039       * @param accessors  An Array of the MethodDatas corresponding to the accessors for this class.
1040       */
1041      protected static void writeSimpleHashCode(String className, SymbolData sd, int baseIndent, boolean waitForVarDef, 
1042                                                String valueHashCodeName, MethodData[] accessors) {
1043        
1044        _writeToFileOut(newLine + indentString(baseIndent, 1) + 
1045                        "/** This method is automatically generated by the Language Level Converter. */");
1046        _writeToFileOut(newLine + indentString(baseIndent, 1) + "public int hashCode() {" + newLine);
1047        _writeToFileOut(indentString(baseIndent, 2) + "return getClass().hashCode()");
1048        for (int i = 0; i < accessors.length; i++) {
1049          _writeToFileOut(" ^ " + newLine + indentString(baseIndent, 4));
1050          SymbolData type = accessors[i].getReturnType().getSymbolData();
1051          
1052          if (! type.isPrimitiveType()) {
1053            _writeToFileOut("(" + accessors[i].getName() + "() == null ? 0 : " + accessors[i].getName() + "().hashCode())");
1054          }
1055          else if (type == SymbolData.BOOLEAN_TYPE) {
1056            _writeToFileOut("(" + accessors[i].getName() + "() ? 1 : 0)");
1057          }
1058          else if (type.isAssignableTo(SymbolData.INT_TYPE, LanguageLevelConverter.OPT.javaVersion())) {
1059            _writeToFileOut(accessors[i].getName() + "()");
1060          }
1061          else {
1062            _writeToFileOut("(int) " + accessors[i].getName() + "()");           
1063          }
1064        }
1065        
1066        _writeToFileOut(";" + newLine);
1067        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1068      }
1069      
1070      /**
1071       * Write a method to generate a String for any Object, including arrays, nulls, and other reference types.
1072       * 
1073       * @param sd  The method's enclosing class.
1074       * @param baseIndent  The base indent level (number of spaces).
1075       * 
1076       * @return  The name of the generated valueToString method (__valueToString by default).
1077       */
1078      private static String writeValueToString(SymbolData sd, int baseIndent) {
1079        String methodName = sd.createUniqueMethodName("__valueToString");
1080        if (_safeSupportCode) { writeSafeValueToString(sd, baseIndent, methodName); }
1081        return methodName;
1082      }
1083      
1084      /** Helper to writeValueToString; writes a valueToString that correctly handles arbitrary arrays. 
1085       * @param sd  The method's enclosing class.
1086       * @param baseIndent  The base indent level (number of spaces to put at the beginning of every line).
1087       * @param methodName  The name of the generated valueToString method (__valueToString by default).
1088       */
1089    
1090      private static void writeSafeValueToString(SymbolData sd, int baseIndent, String methodName) {
1091        String[] primitiveTypes = new String[]{"byte[]", "short[]", "char[]", "int[]", "long[]", "float[]", "double[]",
1092          "boolean[]"};
1093        boolean useGenerics = LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5);
1094        
1095        _writeToFileOut(newLine);
1096        _writeToFileOut(indentString(baseIndent, 1) + "/**" + newLine);
1097        _writeToFileOut(indentString(baseIndent, 1) + 
1098                        " * This method is automatically generated by the LanguageLevelConverter." + newLine);
1099        _writeToFileOut(indentString(baseIndent, 1) + 
1100                        " * As a helper to toString(), it recursively generates a string for any object," + newLine);
1101        _writeToFileOut(indentString(baseIndent, 1) + " * including nulls, arrays, and standard reference types." + newLine);
1102        _writeToFileOut(indentString(baseIndent, 1) + " */" + newLine);
1103        _writeToFileOut(indentString(baseIndent, 1) + "private java.lang.String " + methodName + "(java.lang.Object o) {" +
1104                        newLine + newLine);
1105        _writeToFileOut(indentString(baseIndent, 2) + "class ArrayToString {" + newLine + newLine);
1106        _writeToFileOut(indentString(baseIndent, 3) + "public String valueFor(java.lang.Object o) {" + newLine);
1107        _writeToFileOut(indentString(baseIndent, 4) + "if (o instanceof java.lang.Object[]) {" + newLine);
1108    
1109        if (useGenerics) {
1110          _writeToFileOut(indentString(baseIndent, 5) + 
1111                          "return arrayToString((java.lang.Object[]) o, new java.util.HashSet<java.lang.Object[]>());" + 
1112                          newLine);
1113        }
1114        else {
1115          _writeToFileOut(indentString(baseIndent, 5) + 
1116                          "return arrayToString((java.lang.Object[]) o, new java.util.HashSet());" + newLine);
1117        }
1118        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1119        
1120        for (String type : primitiveTypes) {
1121          _writeToFileOut(indentString(baseIndent, 4) + "else if (o instanceof " + type + ") {" + newLine);
1122          _writeToFileOut(indentString(baseIndent, 5) + "return arrayToString((" + type + ") o);" + newLine);
1123          _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1124        }
1125        
1126        _writeToFileOut(indentString(baseIndent, 4) + "else {" + newLine);
1127        _writeToFileOut(indentString(baseIndent, 5) + "// o should be an array, but if not, toString() is called" + newLine);
1128        _writeToFileOut(indentString(baseIndent, 5) + "return o.toString();" + newLine);
1129        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1130        
1131        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1132        
1133        for (String type : primitiveTypes) {
1134          _writeToFileOut(indentString(baseIndent, 3) + "public java.lang.String arrayToString(" + type + " array) {" +
1135                          newLine);
1136          _writeToFileOut(indentString(baseIndent, 4) + "java.lang.StringBuffer result = new java.lang.StringBuffer();" +
1137                          newLine);
1138          _writeToFileOut(indentString(baseIndent, 4) + "result.append(\"[\");" + newLine);
1139          _writeToFileOut(indentString(baseIndent, 4) + "if (array.length > 0) { result.append(array[0]); }" + newLine);
1140          _writeToFileOut(indentString(baseIndent, 4) + "for (int i = 1; i < array.length; i++) {" + newLine);
1141          _writeToFileOut(indentString(baseIndent, 5) + "result.append(\", \");" + newLine);
1142          _writeToFileOut(indentString(baseIndent, 5) + "result.append(array[i]);" + newLine);
1143          _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1144          _writeToFileOut(indentString(baseIndent, 4) + "result.append(\"]\");" + newLine);
1145          _writeToFileOut(indentString(baseIndent, 4) + "return result.toString();" + newLine);
1146          _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1147        }
1148        
1149        if (useGenerics) {
1150          _writeToFileOut(indentString(baseIndent, 3) + 
1151                          "public java.lang.String arrayToString(java.lang.Object[] array, " + 
1152                          "java.util.HashSet<java.lang.Object[]> alreadyPrinted) {" + newLine);
1153        }
1154        else {
1155          _writeToFileOut(indentString(baseIndent, 3) + 
1156                          "public java.lang.String arrayToString(java.lang.Object[] array, " + 
1157                          "java.util.HashSet alreadyPrinted) {" + newLine);
1158        }
1159        _writeToFileOut(indentString(baseIndent, 4) + 
1160                        "if (alreadyPrinted.contains(array)) { return (\"[...]\"); }" + newLine);
1161        _writeToFileOut(indentString(baseIndent, 4) + "else { alreadyPrinted.add(array); }" + newLine + newLine);
1162        _writeToFileOut(indentString(baseIndent, 4) + 
1163                        "java.lang.StringBuffer result = new java.lang.StringBuffer();" + newLine);
1164        _writeToFileOut(indentString(baseIndent, 4) + "result.append(\"[\");" + newLine);
1165        _writeToFileOut(indentString(baseIndent, 4) + "boolean nonEmpty = false;" + newLine);
1166        _writeToFileOut(indentString(baseIndent, 4) + "for (int i = 0; i < array.length; i++) {" + newLine);
1167        _writeToFileOut(indentString(baseIndent, 5) + "if (nonEmpty) { result.append(\", \"); }" + newLine);
1168        _writeToFileOut(indentString(baseIndent, 5) + "nonEmpty = true;" + newLine + newLine);
1169        _writeToFileOut(indentString(baseIndent, 5) + "if (array[i] instanceof java.lang.Object[]) {" + newLine);
1170        _writeToFileOut(indentString(baseIndent, 6) + 
1171                        "result.append(arrayToString((java.lang.Object[]) array[i], alreadyPrinted));" + newLine);
1172        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1173        _writeToFileOut(indentString(baseIndent, 5) + "else {" + newLine);
1174        _writeToFileOut(indentString(baseIndent, 6) + "result.append(" + methodName + "(array[i]));" + newLine);
1175        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1176        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1177        _writeToFileOut(indentString(baseIndent, 4) + "result.append(\"]\");" + newLine + newLine);
1178        _writeToFileOut(indentString(baseIndent, 4) + "alreadyPrinted.remove(array);" + newLine);
1179        _writeToFileOut(indentString(baseIndent, 4) + "return result.toString();" + newLine);
1180        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1181        
1182        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine + newLine); // end of inner class
1183        _writeToFileOut(indentString(baseIndent, 2) + "if (o == null) { return \"\" + null; }" + newLine);
1184        _writeToFileOut(indentString(baseIndent, 2) + 
1185                        "else if (o.getClass().isArray()) { return new ArrayToString().valueFor(o); }" + newLine);
1186        _writeToFileOut(indentString(baseIndent, 2) + "else { return o.toString(); }" + newLine);
1187        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1188      }
1189      
1190      /** Helper to writeValueToString; writes a simple valueToString that does not handle arrays. 
1191       *  NOTE: This is currently unused.  For the simple case, no valueToString method is generated.
1192       * @param sd  The method's enclosing class.
1193       * @param baseIndent  The base indent level (number of spaces).
1194       */
1195      private static void writeSimpleValueToString(SymbolData sd, int baseIndent, String methodName) {
1196        _writeToFileOut(newLine);
1197        _writeToFileOut(indentString(baseIndent, 1) + "/**" + newLine);
1198        _writeToFileOut(indentString(baseIndent, 1) + 
1199                        " * This method is automatically generated by the LanguageLevelConverter." + newLine);
1200        _writeToFileOut(indentString(baseIndent, 1) + 
1201                        " * As a helper to toString(), it generates a string for any object," + newLine);
1202        _writeToFileOut(indentString(baseIndent, 1) + " * including nulls and standard reference types." + newLine);
1203        _writeToFileOut(indentString(baseIndent, 1) + " */" + newLine);
1204        _writeToFileOut(indentString(baseIndent, 1) + "private java.lang.String " + methodName + "(java.lang.Object o) {" + 
1205                        newLine + newLine);
1206        _writeToFileOut(indentString(baseIndent, 2) + "if (o == null) { return \"\" + null; }" + newLine);
1207        _writeToFileOut(indentString(baseIndent, 2) + "else { return o.toString(); }" + newLine);
1208        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1209      }
1210      
1211      /**
1212       * Write a method to compare any two objects, including arrays, nulls, and other reference types.
1213       * 
1214       * @param sd  The method's enclosing class.
1215       * @param baseIndent  The base indent level (number of spaces).
1216       * 
1217       * @return  The name of the generated valueEquals method (__valueEquals by default).
1218       */
1219      private static String writeValueEquals(SymbolData sd, int baseIndent) {
1220        String methodName = sd.createUniqueMethodName("__valueEquals");
1221        if (_safeSupportCode) { writeSafeValueEquals(sd, baseIndent, methodName); }
1222    //    else { writeSimpleValueEquals(sd, baseIndent, methodName); }
1223        return methodName;
1224      }
1225      
1226      /** Helper to writeValueEquals; writes a valueEquals that correctly handles arbitrary arrays. */
1227      private static void writeSafeValueEquals(SymbolData sd, int baseIndent, String methodName) {
1228        String[] primitiveTypes = new String[]{"byte[]", "short[]", "char[]", "int[]", "long[]", "float[]", "double[]", 
1229          "boolean[]"};
1230        boolean useGenerics = LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5);
1231        
1232        _writeToFileOut(newLine);
1233        _writeToFileOut(indentString(baseIndent, 1) + "/**" + newLine);
1234        _writeToFileOut(indentString(baseIndent, 1) + 
1235                        " * This method is automatically generated by the LanguageLevelConverter." + newLine);
1236        _writeToFileOut(indentString(baseIndent, 1) + 
1237                        " * As a helper to equals(Object), it recursively compares any two objects," + newLine);
1238        _writeToFileOut(indentString(baseIndent, 1) + 
1239                        " * including nulls, arrays, and standard reference types." + newLine);
1240         _writeToFileOut(indentString(baseIndent, 1) + " */" + newLine);
1241        _writeToFileOut(indentString(baseIndent, 1) + "private boolean " + methodName + 
1242                        "(java.lang.Object o1, java.lang.Object o2) {" + newLine + newLine);
1243        _writeToFileOut(indentString(baseIndent, 2) + "class ArrayEquals {" + newLine + newLine);
1244        _writeToFileOut(indentString(baseIndent, 3) + 
1245                        "public boolean valueFor(java.lang.Object o1, java.lang.Object o2) {" + newLine);
1246        _writeToFileOut(indentString(baseIndent, 4) + 
1247                        "if (o1 instanceof java.lang.Object[] && o2 instanceof java.lang.Object[]) {" + newLine);
1248        if (useGenerics) {
1249          _writeToFileOut(indentString(baseIndent, 5) + "return arrayEquals((java.lang.Object[]) o1, " + 
1250                          "(java.lang.Object[]) o2, new java.util.HashSet<java.lang.Object>());" + newLine);
1251        }
1252        else {
1253          _writeToFileOut(indentString(baseIndent, 5) + "return arrayEquals((java.lang.Object[]) o1, " + 
1254                          "(java.lang.Object[]) o2, new java.util.HashSet());" + newLine);
1255        }
1256        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1257        
1258        for (String type : primitiveTypes) {
1259          _writeToFileOut(indentString(baseIndent, 4) + "else if (o1 instanceof " + type + " && o2 instanceof " + 
1260                          type + ") {" + newLine);
1261          _writeToFileOut(indentString(baseIndent, 5) + "return arrayEquals((" + type + ") o1, (" + type + ") o2);" + 
1262                          newLine);
1263          _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1264        }
1265        
1266        _writeToFileOut(indentString(baseIndent, 4) + "else {" + newLine);
1267        _writeToFileOut(indentString(baseIndent, 5) + "// o1 and o2 should be arrays, but if not, " + 
1268                        "or if they have different types, equals(Object) is called" + newLine); 
1269        _writeToFileOut(indentString(baseIndent, 5) + "return o1.equals(o2);" + newLine); 
1270        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1271        
1272        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1273        
1274        for (String type : primitiveTypes) {
1275          _writeToFileOut(indentString(baseIndent, 3) + "public boolean arrayEquals(" + type + " array1, " + type + 
1276                          " array2) {" + newLine);
1277          _writeToFileOut(indentString(baseIndent, 4) + "if (array1.length != array2.length) { return false; }" +
1278                          newLine + newLine);
1279          _writeToFileOut(indentString(baseIndent, 4) + "else {" + newLine);
1280          _writeToFileOut(indentString(baseIndent, 5) + "for (int i = 0; i < array1.length; i++) {" + newLine);
1281          _writeToFileOut(indentString(baseIndent, 6) + "if (array1[i] != array2[i]) { return false; }" + newLine);
1282          _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1283          _writeToFileOut(indentString(baseIndent, 5) + "return true;" + newLine);
1284          _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1285          _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1286        }
1287        
1288        if (useGenerics) {
1289          _writeToFileOut(indentString(baseIndent, 3) + "public boolean arrayEquals(final java.lang.Object[] array1, " + 
1290                          "final java.lang.Object[] array2, java.util.HashSet<java.lang.Object> alreadyCompared) {" + 
1291                          newLine + newLine);
1292        }
1293        else {
1294          _writeToFileOut(indentString(baseIndent, 3) + "public boolean arrayEquals(final java.lang.Object[] array1," +
1295                          " final java.lang.Object[] array2, java.util.HashSet alreadyCompared) {" + newLine + newLine);
1296        }
1297        _writeToFileOut(indentString(baseIndent, 4) + "class ArrayPair {" + newLine);
1298        _writeToFileOut(indentString(baseIndent, 5) + "public java.lang.Object[] array1() { return array1; }" + newLine);
1299        _writeToFileOut(indentString(baseIndent, 5) + "public java.lang.Object[] array2() { return array2; }" + newLine);
1300        _writeToFileOut(indentString(baseIndent, 5) + "public boolean equals(java.lang.Object o) {" + newLine);
1301        _writeToFileOut(indentString(baseIndent, 6) + "if ((o == null) || ! (o instanceof ArrayPair)) { return false; }" + 
1302                        newLine);
1303        _writeToFileOut(indentString(baseIndent, 6) + 
1304                        "else { return (array1.equals(((ArrayPair) o).array1())) && " + 
1305                        "(array2.equals(((ArrayPair) o).array2())); }" + newLine);
1306        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1307        _writeToFileOut(indentString(baseIndent, 5) + 
1308                        "public int hashCode() { return array1.hashCode() ^ (array2.hashCode() << 1); }" + newLine);
1309        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine + newLine);
1310        _writeToFileOut(indentString(baseIndent, 4) + 
1311                        "if (array1.length != array2.length) { return false; }" + newLine + newLine);
1312        _writeToFileOut(indentString(baseIndent, 4) + "else {" + newLine);
1313        _writeToFileOut(indentString(baseIndent, 5) + "ArrayPair currentPair = new ArrayPair();" + newLine);
1314        _writeToFileOut(indentString(baseIndent, 5) + "if (alreadyCompared.contains(currentPair)) { return true; }" + 
1315                        newLine);
1316        _writeToFileOut(indentString(baseIndent, 5) + "alreadyCompared.add(currentPair);" + newLine + newLine);
1317        _writeToFileOut(indentString(baseIndent, 5) + "boolean result = true;" + newLine);
1318        _writeToFileOut(indentString(baseIndent, 5) + "for (int i = 0; i < array1.length; i++) {" + newLine);
1319        _writeToFileOut(indentString(baseIndent, 6) + 
1320                        "if (array1[i] instanceof java.lang.Object[] && array2[i] instanceof java.lang.Object[]) {" + 
1321                        newLine);
1322        _writeToFileOut(indentString(baseIndent, 7) + 
1323                        "result = arrayEquals((java.lang.Object[]) array1[i], " + 
1324                        "(java.lang.Object[]) array2[i], alreadyCompared);" + 
1325                        newLine);
1326        _writeToFileOut(indentString(baseIndent, 6) + "}" + newLine);
1327        _writeToFileOut(indentString(baseIndent, 6) + "else {" + newLine);
1328        _writeToFileOut(indentString(baseIndent, 7) + "result = " + methodName + "(array1[i], array2[i]);"+ newLine);
1329        _writeToFileOut(indentString(baseIndent, 6) + "}" + newLine + newLine);
1330        _writeToFileOut(indentString(baseIndent, 6) + "if (!result) { break; }" + newLine);
1331        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine + newLine);
1332        _writeToFileOut(indentString(baseIndent, 5) + "alreadyCompared.remove(currentPair);" + newLine);
1333        _writeToFileOut(indentString(baseIndent, 5) + "return result;" + newLine);
1334        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1335        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1336    
1337        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine + newLine); // end of inner class
1338        _writeToFileOut(indentString(baseIndent, 2) + "if (o1 == null) { return o2 == null; }" + newLine);
1339        _writeToFileOut(indentString(baseIndent, 2) + "else if (o2 == null) { return false; }" + newLine);
1340        _writeToFileOut(indentString(baseIndent, 2) + 
1341                        "else if (o1.getClass().isArray() && o2.getClass().isArray()) " +
1342                        "{ return new ArrayEquals().valueFor(o1, o2); }" + newLine);
1343        _writeToFileOut(indentString(baseIndent, 2) + "else { return o1.equals(o2); }" + newLine);
1344        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1345      }
1346      
1347      /** Helper to writeValueEquals; writes a simple valueEquals that does not handle arrays. 
1348       *  NOTE: This is currently unused.  For the simple case, no valueEquals method is generated.
1349       */
1350      private static void writeSimpleValueEquals(SymbolData sd, int baseIndent, String methodName) {
1351        _writeToFileOut(newLine);
1352        _writeToFileOut(indentString(baseIndent, 1) + "/**" + newLine);
1353        _writeToFileOut(indentString(baseIndent, 1) + 
1354                        " * This method is automatically generated by the LanguageLevelConverter." + newLine);
1355        _writeToFileOut(indentString(baseIndent, 1) + 
1356                        " * As a helper to equals(Object), it compares any two objects," + newLine);
1357        _writeToFileOut(indentString(baseIndent, 1) + " * including nulls and standard reference types." + newLine);
1358        _writeToFileOut(indentString(baseIndent, 1) + " */" + newLine);
1359        _writeToFileOut(indentString(baseIndent, 1) + "private boolean " +
1360                        methodName + "(java.lang.Object o1, java.lang.Object o2) {" + newLine + newLine);
1361        _writeToFileOut(indentString(baseIndent, 2) + "if (o1 == null) { return o2 == null; }" + newLine);
1362        _writeToFileOut(indentString(baseIndent, 2) + "else if (o2 == null) { return false; }" + newLine);
1363        _writeToFileOut(indentString(baseIndent, 2) + "else { return o1.equals(o2); }" + newLine);
1364        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1365      }
1366      
1367      /** Write a method to generate a hash code for any Object, including arrays, nulls, and other reference types. 
1368        * @param sd  The method's enclosing class.
1369        * @param baseIndent  The base indent level (number of spaces).
1370        * @param valueEqualsName  The name of the method generated by writeValueEquals()
1371        * 
1372        * @return  The name of the generated valueHashCode method (__valueHashCode by default).
1373        */
1374      private static String writeValueHashCode(SymbolData sd, int baseIndent, String valueEqualsName) {
1375        String methodName = sd.createUniqueMethodName("__valueHashCode");
1376        if (_safeSupportCode) { writeSafeValueHashCode(sd, baseIndent, valueEqualsName, methodName); }
1377    //    else { writeSimpleValueHashCode(sd, baseIndent, valueEqualsName, methodName); }
1378        return methodName;
1379      }
1380      
1381      /** Helper to writeValueHashCode; writes a valueHashCode that correctly handles arbitrary arrays. */
1382      private static void writeSafeValueHashCode(SymbolData sd, int baseIndent, String valueEqualsName, String methodName) {
1383        String[] primitiveTypes =
1384          new String[]{"byte[]", "short[]", "char[]", "int[]", "long[]", "float[]", "double[]", "boolean[]"};
1385        boolean useGenerics = LanguageLevelConverter.OPT.javaVersion().supports(JavaVersion.JAVA_5);
1386        
1387        _writeToFileOut(newLine);
1388        _writeToFileOut(indentString(baseIndent, 1) + "/**" + newLine);
1389        _writeToFileOut(indentString(baseIndent, 1) + 
1390                        " * This method is automatically generated by the LanguageLevelConverter." + newLine);
1391        _writeToFileOut(indentString(baseIndent, 1) + 
1392                        " * As a helper to hashCode(), it recursively generates a hash code for any object," + newLine);
1393        _writeToFileOut(indentString(baseIndent, 1) +
1394                        " * including nulls, arrays, and standard reference types." + newLine);
1395         _writeToFileOut(indentString(baseIndent, 1) + " */" + newLine);
1396        _writeToFileOut(indentString(baseIndent, 1) + "private int " + methodName + "(java.lang.Object o) {" + 
1397                        newLine + newLine);
1398        _writeToFileOut(indentString(baseIndent, 2) + "class ArrayHashCode {" + newLine + newLine);
1399        _writeToFileOut(indentString(baseIndent, 3) + "public int valueFor(java.lang.Object o) {" + newLine);
1400        _writeToFileOut(indentString(baseIndent, 4) + "if (o instanceof java.lang.Object[]) {" + newLine);
1401        if (useGenerics) {
1402          _writeToFileOut(indentString(baseIndent, 5) + 
1403                          "return arrayHashCode((java.lang.Object[]) o, new java.util.LinkedList<java.lang.Object>());" + 
1404                          newLine);
1405        }
1406        else {
1407          _writeToFileOut(indentString(baseIndent, 5) + 
1408                          "return arrayHashCode((java.lang.Object[]) o, new java.util.LinkedList());" + newLine);
1409        }
1410        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1411        
1412        for (String type : primitiveTypes) {
1413          _writeToFileOut(indentString(baseIndent, 4) + "else if (o instanceof " + type + ") {" + newLine);
1414          _writeToFileOut(indentString(baseIndent, 5) + "return arrayHashCode((" + type + ") o);" + newLine);
1415          _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1416        }
1417        
1418        _writeToFileOut(indentString(baseIndent, 4) + "else {" + newLine);
1419        _writeToFileOut(indentString(baseIndent, 5) + "// o should be an array, but if not, hashCode() is called" + 
1420                        newLine);
1421        _writeToFileOut(indentString(baseIndent, 5) + "return o.hashCode();" + newLine); 
1422        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1423        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1424        
1425        for (String type : primitiveTypes) {
1426          _writeToFileOut(indentString(baseIndent, 3) + "public int arrayHashCode(" + type + " array) {" + newLine);
1427          _writeToFileOut(indentString(baseIndent, 4) + "int result = 0;" + newLine);
1428          _writeToFileOut(indentString(baseIndent, 4) + "for (int i = 0; i < array.length; i++) {" + newLine);
1429          if (type.equals("boolean[]")) {
1430            _writeToFileOut(indentString(baseIndent, 5) + "result = (result << 1) ^ (array[i] ? 1 : 0);" + newLine);
1431          }
1432          else {
1433            _writeToFileOut(indentString(baseIndent, 5) + "result = (result << 1) ^ (int) array[i];" + newLine);
1434          }
1435          _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine);
1436          _writeToFileOut(indentString(baseIndent, 4) + "return result;" + newLine);
1437          _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1438        }
1439        
1440        if (useGenerics) {
1441          _writeToFileOut(indentString(baseIndent, 3) + "public int arrayHashCode(final java.lang.Object[] array, " + 
1442                          "final java.util.LinkedList<java.lang.Object> alreadyGenerated) {" + newLine + newLine);
1443        }
1444        else {
1445          _writeToFileOut(indentString(baseIndent, 3) + "public int arrayHashCode(final java.lang.Object[] array, " + 
1446                          "final java.util.LinkedList alreadyGenerated) {" + newLine + newLine);
1447        }
1448        _writeToFileOut(indentString(baseIndent, 4) + "class ArrayWrapper {" + newLine);
1449        _writeToFileOut(indentString(baseIndent, 5) + "public java.lang.Object[] array() { return array; }" + newLine);
1450        _writeToFileOut(indentString(baseIndent, 5) + "public boolean equals(java.lang.Object o) {" + newLine);
1451        _writeToFileOut(indentString(baseIndent, 6) + "return (o != null) && (o instanceof ArrayWrapper)  && " + 
1452                        valueEqualsName + "(array, ((ArrayWrapper) o).array());" + newLine);
1453        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1454        _writeToFileOut(indentString(baseIndent, 5) + 
1455                        "public int hashCode() { return 0; } // This method should never be used -- " + 
1456                        "only here for consistency." + newLine);
1457        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine + newLine);
1458    
1459        _writeToFileOut(indentString(baseIndent, 4) + "ArrayWrapper currentWrapper = new ArrayWrapper();" + newLine);
1460        _writeToFileOut(indentString(baseIndent, 4) + "if (alreadyGenerated.contains(currentWrapper)) { return -1; }" + 
1461                        newLine);
1462        _writeToFileOut(indentString(baseIndent, 4) + "alreadyGenerated.addLast(currentWrapper);" + newLine + newLine);
1463        
1464        _writeToFileOut(indentString(baseIndent, 4) + "int result = 0;" + newLine);
1465        _writeToFileOut(indentString(baseIndent, 4) + "for (int i = 0; i < array.length; i++) {" + newLine);
1466        _writeToFileOut(indentString(baseIndent, 5) + "if (array[i] instanceof java.lang.Object[]) {" + newLine);
1467        _writeToFileOut(indentString(baseIndent, 6) +
1468                        "result = (result << 1) ^ (arrayHashCode((java.lang.Object[]) array[i], alreadyGenerated) >> 1);" +
1469                        newLine);
1470        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1471        _writeToFileOut(indentString(baseIndent, 5) + "else {" + newLine);
1472        _writeToFileOut(indentString(baseIndent, 6) + "result = (result << 1) ^ " + methodName + "(array[i]);" + newLine);
1473        _writeToFileOut(indentString(baseIndent, 5) + "}" + newLine);
1474        _writeToFileOut(indentString(baseIndent, 4) + "}" + newLine + newLine);
1475        _writeToFileOut(indentString(baseIndent, 4) + "alreadyGenerated.removeLast();" + newLine);
1476        _writeToFileOut(indentString(baseIndent, 4) + "return result;" + newLine);
1477        _writeToFileOut(indentString(baseIndent, 3) + "}" + newLine + newLine);
1478        
1479        _writeToFileOut(indentString(baseIndent, 2) + "}" + newLine + newLine); // end of inner class
1480        _writeToFileOut(indentString(baseIndent, 2) + "if (o == null) { return 0; }" + newLine);
1481        _writeToFileOut(indentString(baseIndent, 2) + 
1482                        "else if (o.getClass().isArray()) { return new ArrayHashCode().valueFor(o); }" + newLine);
1483        _writeToFileOut(indentString(baseIndent, 2) + "else { return o.hashCode(); }" + newLine);
1484        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1485      }
1486      
1487      /** Helper to writeValueHashCode; writes a valueHashCode that does not handle arrays. 
1488       *  NOTE: This is currently unused.  For the simple case, no valueHashCode method is generated.
1489       */
1490      private static void writeSimpleValueHashCode(SymbolData sd, int baseIndent, String valueEqualsName, 
1491                                                   String methodName) {
1492        _writeToFileOut(newLine);
1493        _writeToFileOut(indentString(baseIndent, 1) + "/**" + newLine);
1494        _writeToFileOut(indentString(baseIndent, 1) + 
1495                        " * This method is automatically generated by the LanguageLevelConverter." + newLine);
1496        _writeToFileOut(indentString(baseIndent, 1) + 
1497                        " * As a helper to hashCode(), it generates a hash code for any object," + newLine);
1498        _writeToFileOut(indentString(baseIndent, 1) + " * including nulls and standard reference types." + newLine);
1499         _writeToFileOut(indentString(baseIndent, 1) + " */" + newLine);
1500        _writeToFileOut(indentString(baseIndent, 1) + "private int " + methodName + "(java.lang.Object o) {" + 
1501                        newLine + newLine);
1502        _writeToFileOut(indentString(baseIndent, 2) + "if (o == null) { return 0; }" + newLine);
1503        _writeToFileOut(indentString(baseIndent, 2) + "else { return o.hashCode(); }" + newLine);
1504        _writeToFileOut(indentString(baseIndent, 1) + "}" + newLine);
1505      }
1506      
1507      private static String indentString(int baseIndent, int indentCount) {
1508        int length = indentCount * indentWidth + baseIndent;
1509        StringBuffer result = new StringBuffer(length);
1510        for (int i = 0; i < length; i++) {
1511          result.append(' ');
1512        }
1513        return result.toString();
1514      }
1515      
1516    //  private static boolean _isElementaryFile()   { return LanguageLevelConverter.isElementaryFile(_llv._file); }
1517    //  private static boolean _isIntermediateFile() { return LanguageLevelConverter.isIntermediateFile(_llv._file); }
1518    //  private static boolean _isAdvancedFile()     { return LanguageLevelConverter.isAdvancedFile(_llv._file); }
1519      
1520      private static LinkedList<MethodData> _getVariableAccessorListHelper(SymbolData currClass) {
1521        List<Pair<VariableData, MethodData>> accessorMappings = new Vector<Pair<VariableData, MethodData>>();
1522        LinkedList<SymbolData> classes = new LinkedList<SymbolData>();
1523        classes.add(currClass);
1524        
1525        // Gather all accessor methods that have a matching signature with their variables
1526        while (classes.size() > 0) {
1527          SymbolData tempSd = classes.removeFirst();
1528          if (LanguageLevelVisitor.isJavaLibraryClass(tempSd.getName())) { break; }
1529          
1530          for (int i = 0; i<tempSd.getVars().size(); i++) {
1531            VariableData tempVd = tempSd.getVars().get(i);
1532            MethodData md = tempSd.getMethod(tempVd.getName(), new TypeData[0]);
1533            if (md != null)
1534              accessorMappings.add(new Pair<VariableData, MethodData>(tempVd, md));
1535          }
1536          // Note that we don't need to check interface fields, because they are always static.
1537          
1538          // Insure that we traverse the superclass hierarchy before we traverse the outer class hierarchy    
1539          SymbolData superClass = tempSd.getSuperClass();
1540          if (superClass != null) classes.addFirst(superClass); 
1541          Data outerData = tempSd.getOuterData();
1542          if (outerData != null) { classes.addLast(outerData.getSymbolData()); }
1543        }
1544      
1545        // Eliminate those accessors that are inaccessible, that throw exceptions, that are static, that are shadowed, 
1546        // or that have a different return type
1547        LinkedList<MethodData> allMethods = new LinkedList<MethodData>();
1548        for (int i = accessorMappings.size() - 1; i >= 0; i--) {
1549          VariableData vd = accessorMappings.get(i).getFirst();
1550          MethodData md = accessorMappings.get(i).getSecond();
1551          boolean canSeeMethod =
1552            TypeChecker.checkAccess(new NullLiteral(SourceInfo.NONE), md.getMav(), md.getName(), 
1553                                           md.getSymbolData(), currClass, "method", false);
1554          //TODO: it is okay to throw Runtime exceptions or Errors) {
1555          if (canSeeMethod && (! md.hasModifier("static")) && (md.getThrown().length == 0) &&
1556              vd.getType().getSymbolData().isAssignableTo(md.getReturnType(), LanguageLevelConverter.OPT.javaVersion())) {
1557            boolean isShadowed = false;
1558            for (int j = i - 1; j >= 0; j--) {
1559              if (accessorMappings.get(j).getSecond().getName().equals(md.getName())) { isShadowed = true; break; }
1560            }
1561            if (!isShadowed) { allMethods.addFirst(md); }
1562          }
1563        }
1564        return allMethods;
1565      }
1566    
1567      /** Reads _fileIn through the given (line, column) returning this text.  On completion, the current cursor 
1568        * (_fileInLine, fileInColumn) is one character after (line, column).
1569        * @param line The line number to read through.
1570        * @param column The column to read to (or 0 to read to through the end of the previous line).
1571        */
1572      private static String _readThroughIndex(int line, int column) {
1573        if (_fileInLine > line || (_fileInLine == line && _fileInColumn - 1 > column)) {
1574          throw new RuntimeException("Internal Program Error: Attempt to read in " + _llv._file.getName() + 
1575                                     " at a point that is already past: line " + line + ", column " + column + 
1576                                     "; (currently at " + _fileInLine + ", " + _fileInColumn + ").  Please report this bug.");
1577        }
1578        
1579        try {
1580          StringBuffer result = new StringBuffer();
1581          while (_fileInLine < line) {
1582            String l = _fileIn.readLine();
1583            if (l == null) {
1584              _fileOut.flush();
1585              throw new RuntimeException("Internal Program Error: Attempt to read in " + _llv._file.getName() + 
1586                                         " past the end of file: line " + line + ", column " + column + "; (currently at " +
1587                                         _fileInLine + ", " + _fileInColumn + ").  Please report this bug.");
1588            }
1589            
1590            result.append(l).append(newLine);
1591            
1592            _fileInLine++;
1593            _fileInColumn = 1;
1594          }
1595          
1596          int lastLineLength = column - _fileInColumn + 1;
1597          char[] chars = new char[lastLineLength];
1598          int charsRead = _fileIn.read(chars, 0, lastLineLength);
1599          if (charsRead != lastLineLength) {
1600            _fileOut.flush();
1601            throw new RuntimeException("Internal Program Error: Attempt to read in " + _llv._file.getName() + 
1602                                       " past the end of file: line " + line + ", column " + column + "; (currently at " +
1603                                       _fileInLine + ", " + _fileInColumn + ").  Please report this bug.");
1604          }
1605          result.append(chars);
1606          _fileInLine = line;
1607          _fileInColumn = column + 1;
1608          return result.toString();
1609        }
1610        catch (IOException ioe) { throw new Augmentor.Exception(ioe); }
1611      }
1612        
1613      /** Reads _fileIn through the given line & column and write to output.  On completion, the current cursor is one 
1614        * character after (line, column).
1615        * @param line The line number to read through.
1616        * @param column The column to read to (or 0 to read to through the end of the previous line).
1617        */
1618      private static void _readAndWriteThroughIndex(int line, int column) {
1619        String text = _readThroughIndex(line, column);
1620        _writeToFileOut(text, true); // yes, writing straight from input
1621      }
1622      
1623      private static void _writeToFileOut(String s) { _writeToFileOut(s, false); }
1624      
1625      /** Write the string to _fileOut. If fromInput is true, the string is coming straight from the input file,
1626        * which means the corresponding line number should be incremented as well.
1627        * @param s The string to write.
1628        * @param fromInput true if the corresponding line number should be incremented as well */
1629      private static void _writeToFileOut(String s, boolean fromInput) {
1630        try {
1631          String[] lines = s.split(newLine, -1);
1632          for(int i=0; i<lines.length-1; ++i) {
1633            _fileOut.write(lines[i]);
1634            // add line number to map if it doesn't exist yet
1635            if (_lineNumberMap.get(_fileOutCorrespondingLine)==null) 
1636              _lineNumberMap.put(_fileOutCorrespondingLine, _fileOutLine);
1637            // end-of-line line number mapping; disabled since we output the entire map at the beginning of the file
1638            // _fileOut.write("//["+_fileOutCorrespondingLine+"]");
1639            _fileOut.write(newLine);
1640            ++_fileOutLine;
1641            if (fromInput) ++_fileOutCorrespondingLine; // true if we are copying straight from input
1642          }
1643          _fileOut.write(lines[lines.length-1]);
1644        }
1645        catch (IOException ioe) { throw new Augmentor.Exception(ioe); }
1646      }
1647      
1648      /** Reads _fileIn through the specified (line, column) but leaves the file cursor unchanged.
1649        * @param line The line number to read through.
1650        * @param column The column to read to (or 0 to read to through the end of the previous line).
1651        */
1652      private static String _peek(int line, int column) {
1653        
1654        try {
1655          _fileIn.mark(LanguageLevelConverter.INPUT_BUFFER_SIZE);
1656          // Save the cursor
1657          int fileInLine = _fileInLine;
1658          int fileInColumn = _fileInColumn;
1659          String text = _readThroughIndex(line, column);
1660          _fileIn.reset();
1661          // Reset the cursor
1662          _fileInLine = fileInLine;
1663          _fileInColumn = fileInColumn;
1664          return text;
1665          }
1666          catch (IOException ioe) { throw new Augmentor.Exception(ioe); }
1667      }
1668      
1669      /** Returns a copy of the line number map that maps original dj* line numbers
1670        * to generated java line numbers.
1671        * @return copy of line number map */
1672      public static SortedMap<Integer,Integer> getLineNumberMap() {
1673        return new TreeMap<Integer,Integer>(_lineNumberMap);
1674      }
1675      
1676      public static class MethodBodyAugmentor extends Augmentor {
1677        
1678        /** Mandatory forwarding constructor. */
1679        protected MethodBodyAugmentor(SymbolData enclosing) { super(enclosing); }
1680        
1681        /** Writes out implicit variableDeclarationModfiers that must be added to augmented file. */
1682        protected void augmentVariableDeclarationModifiers(VariableDeclaration that) { _writeToFileOut("final "); }
1683      }
1684      
1685      public static class Exception extends RuntimeException {
1686        public Exception(java.lang.Exception nested) { super(nested); }
1687      }
1688      
1689      /** Test class for the Augmentor class. */
1690      public static class AugmentorTest extends TestCase {
1691    
1692        public AugmentorTest() {
1693          this("");
1694        }
1695        public AugmentorTest(String name) {
1696          super(name);
1697        }
1698        
1699        private Augmentor _a;
1700        private Symboltable _s = LanguageLevelConverter.symbolTable; // Define a short synonym
1701        private File _f = new File("");
1702        
1703        public void setUp() {
1704          LanguageLevelVisitor llv =
1705            new IntermediateVisitor(_f, 
1706                                    new LinkedList<Pair<String, JExpressionIF>>(), 
1707                                    new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1708                                    new LinkedList<Command>(),
1709                                    new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>());
1710          _a = new Augmentor(true, null, null, llv);
1711          LanguageLevelConverter.symbolTable.clear();
1712          Symboltable _s = LanguageLevelConverter.symbolTable;   
1713          LanguageLevelConverter.OPT = new Options(JavaVersion.JAVA_5, EmptyIterable.<File>make());
1714        }
1715    
1716        public void testFormalParameters2TypeDatas() {
1717          FormalParameter[] fp = new FormalParameter[0];
1718          TypeData[] result = formalParameters2TypeDatas(fp, _a._enclosingData);
1719          assertEquals("The result is empty", 0, result.length);
1720          
1721          PrimitiveType intt = new PrimitiveType(SourceInfo.NONE, "int");
1722          FormalParameter param = 
1723            new FormalParameter(SourceInfo.NONE,
1724                                new UninitializedVariableDeclarator(SourceInfo.NONE, intt, 
1725                                                                    new Word(SourceInfo.NONE, "j")), false);
1726          SymbolData intData = SymbolData.INT_TYPE;
1727          _s.put("int", intData);
1728          
1729          ClassOrInterfaceType stringt = new ClassOrInterfaceType(SourceInfo.NONE, "java.lang.String", new Type[0]);
1730          FormalParameter param2 =
1731            new FormalParameter(SourceInfo.NONE, 
1732                                new UninitializedVariableDeclarator(SourceInfo.NONE, stringt, 
1733                                                                    new Word(SourceInfo.NONE, "j")), false);
1734          SymbolData stringData = new SymbolData("java.lang.String");
1735          _s.put("java.lang.String", stringData);
1736    
1737          fp = new FormalParameter[]{ param, param2 };
1738          result = formalParameters2TypeDatas(fp, _a._enclosingData);
1739          assertTrue("Arrays should be equal", 
1740                     LanguageLevelVisitor.arrayEquals(result, new TypeData[]{ intData, stringData }));
1741          
1742          UninitializedVariableDeclarator vd =
1743            new UninitializedVariableDeclarator(SourceInfo.NONE, 
1744                                                new ClassOrInterfaceType(SourceInfo.NONE, "Inner", new Type[0]), 
1745                                                new Word(SourceInfo.NONE, "t"));
1746          //test an inner class
1747          FormalParameter param3 = 
1748            new FormalParameter(SourceInfo.NONE, vd, false);
1749          fp = new FormalParameter[] {param3};
1750          SymbolData inner = new SymbolData("Inner");
1751          inner.setIsContinuation(false);
1752          _a._enclosingData = new SymbolData("me");
1753          _a._enclosingData.addInnerClass(inner);
1754          result = formalParameters2TypeDatas(fp, _a._enclosingData);
1755          assertTrue("Arrays should be equal", LanguageLevelVisitor.arrayEquals(result, new TypeData[] {inner}));
1756        }
1757    
1758    
1759        public void testIndentString() {
1760          assertEquals("Should return a string of 0 tabs", "", indentString(0, 0));
1761          assertEquals("Should return a string of 6 tabs", "            ", indentString(2, 5));
1762        }
1763    
1764    //    public void testIsElementaryFile() {
1765    //      _llv = new ElementaryVisitor(new File("elementary.dj0"), new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1766    //                                   new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(), 
1767    //                                   new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(), 
1768    //                                   new Hashtable<SymbolData, LanguageLevelVisitor>());
1769    //      assertTrue("This is an elementary file", _isElementaryFile());
1770    //      _llv = new IntermediateVisitor(new File("intermediate.dj1"), 
1771    //                                     new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1772    //                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1773    //                                     new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1774    //                                     new Hashtable<SymbolData, LanguageLevelVisitor>());
1775    //      assertFalse("This is an intermediate file", _isElementaryFile());
1776    //      _llv = new AdvancedVisitor(new File("advanced.dj2"),
1777    //                                 new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1778    //                                 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1779    //                                 new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1780    //                                 new Hashtable<SymbolData, LanguageLevelVisitor>());
1781    //      assertFalse("This is an advanced file", _isElementaryFile());
1782    //      _llv = new ElementaryVisitor(new File("full.java"), 
1783    //                                   new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1784    //                                   new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(), 
1785    //                                   new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(), 
1786    //                                   new Hashtable<SymbolData, LanguageLevelVisitor>());
1787    //      assertFalse("This is a full file", _isElementaryFile());
1788    //    }
1789    
1790    //    public void testIsIntermediateFile() {
1791    //      _llv = new ElementaryVisitor(new File("elementary.dj0"), 
1792    //                                   new LinkedList<Pair<String, JExpressionIF>>(), _s,
1793    //                                   new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1794    //                                   new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1795    //                                   new Hashtable<SymbolData, LanguageLevelVisitor>());
1796    //      assertFalse("This is an elementary file", _isIntermediateFile());
1797    //      _llv = new IntermediateVisitor(new File("intermediate.dj1"),
1798    //                                     new LinkedList<Pair<String, JExpressionIF>>(), _s,
1799    //                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1800    //                                     new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1801    //                                     new Hashtable<SymbolData, LanguageLevelVisitor>());
1802    //      assertTrue("This is an intermediate file", _isIntermediateFile());
1803    //      _llv = new AdvancedVisitor(new File("advanced.dj2"),
1804    //                                 new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1805    //                                 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1806    //                                 new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1807    //                                 new Hashtable<SymbolData, LanguageLevelVisitor>());
1808    //      assertFalse("This is an advanced file", _isIntermediateFile());
1809    //      _llv = new ElementaryVisitor(new File("full.java"),
1810    //                                   new LinkedList<Pair<String, JExpressionIF>>(), _s,
1811    //                                   new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1812    //                                   new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1813    //                                   new Hashtable<SymbolData, LanguageLevelVisitor>());
1814    //      assertFalse("This is a full file", _isIntermediateFile());
1815    //    }
1816    //
1817    //    public void testIsAdvancedFile() {
1818    //      _llv = new ElementaryVisitor(new File("elementary.dj0"),
1819    //                                   new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1820    //                                   new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1821    //                                   new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1822    //                                   new Hashtable<SymbolData, LanguageLevelVisitor>());
1823    //      assertFalse("This is an elementary file", _isAdvancedFile());
1824    //      _llv = new IntermediateVisitor(new File("intermediate.dj1"), 
1825    //                                     new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1826    //                                     new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(), 
1827    //                                     new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(), 
1828    //                                     new Hashtable<SymbolData, LanguageLevelVisitor>());
1829    //      assertFalse("This is an intermediate file", _isAdvancedFile());
1830    //      _llv = new AdvancedVisitor(new File("advanced.dj2"), 
1831    //                                 new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1832    //                                 new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
1833    //                                 new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(),
1834    //                                 new Hashtable<SymbolData, LanguageLevelVisitor>());
1835    //      assertTrue("This is an advanced file", _isAdvancedFile());
1836    //      _llv = new ElementaryVisitor(new File("full.java"), 
1837    //                                   new LinkedList<Pair<String, JExpressionIF>>(), _s, 
1838    //                                   new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(), 
1839    //                                   new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>(), 
1840    //                                   new Hashtable<SymbolData, LanguageLevelVisitor>());
1841    //      assertFalse("This is a full file", _isAdvancedFile());
1842    //    }
1843    //    
1844        public void testGetVariableAccessorListHelper() {
1845          ModifiersAndVisibility _publicMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"public"});
1846          ModifiersAndVisibility _privateMav = new ModifiersAndVisibility(SourceInfo.NONE, new String[] {"private"});
1847    
1848          SymbolData houston = new SymbolData("Houston");
1849          SymbolData texas = new SymbolData("Texas");
1850          houston.setSuperClass(texas);
1851          
1852          //first, add a field to texas that has a public gettor.  This one should be returned.
1853          texas.addVar(new VariableData("lone_star", _publicMav, SymbolData.INT_TYPE, true, texas));
1854          MethodData lone_star = new MethodData("lone_star", _publicMav, 
1855                                                new TypeParameter[0], SymbolData.INT_TYPE, 
1856                                                new VariableData[0], 
1857                                                new String[0], texas, 
1858                                                new NullLiteral(SourceInfo.NONE));
1859          texas.addMethod(lone_star); 
1860    
1861          //no gettor for cool, therefore, it should not be returned.
1862          //NOTE: gettor might be best spelled "getter", but whoever is typing believes it should be spelled this way.
1863          texas.addVar(new VariableData("cool", _publicMav, SymbolData.DOUBLE_TYPE, true, texas));
1864    
1865          //now, add a private gettor for armidillo to texas.  This should not be returned, because it is private.
1866          texas.addVar(new VariableData("armadillo", _publicMav, SymbolData.BOOLEAN_TYPE, true, texas));
1867          MethodData armadillo = new MethodData("armadillo", _privateMav, 
1868                                                new TypeParameter[0], 
1869                                                SymbolData.BOOLEAN_TYPE,
1870                                                new VariableData[0], 
1871                                                new String[0], texas, 
1872                                                new NullLiteral(SourceInfo.NONE));
1873          texas.addMethod(armadillo);
1874          
1875          // Now add a field badRoad to Houston.  Its gettor returns a supertype of its type, so it is okay to call.  
1876          // Will be returned.
1877          houston.addVar(new VariableData("badRoad", _publicMav, SymbolData.CHAR_TYPE, true, houston));
1878          MethodData badRoad = new MethodData("badRoad", _publicMav,
1879                                              new TypeParameter[0],
1880                                              SymbolData.INT_TYPE,
1881                                              new VariableData[0], 
1882                                              new String[0], houston, 
1883                                              new NullLiteral(SourceInfo.NONE));
1884          houston.addMethod(badRoad);
1885          
1886          LinkedList<MethodData> expected = new LinkedList<MethodData>();
1887          expected.add(badRoad);
1888          expected.add(lone_star);
1889          LinkedList<MethodData> actual = _getVariableAccessorListHelper(houston);
1890    //      for(MethodData m : actual)
1891    //        System.out.println(m.getName());
1892          assertEquals("Should return the right list of gettors", expected, actual);
1893          
1894          
1895          //if there aren't any fields or methods, that's still okay!
1896          SymbolData soLonely = new SymbolData("I have no fields!");
1897          assertEquals("Should return an empty list", 0, _getVariableAccessorListHelper(soLonely).size());
1898        }
1899        
1900      }
1901    }