Code Search for Developers
 
 
  

LanguageLevelConverter.java from DrJava at Krugle


Show LanguageLevelConverter.java syntax highlighted

/*BEGIN_COPYRIGHT_BLOCK
 *
 * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software is Open Source Initiative approved Open Source Software.
 * Open Source Initative Approved is a trademark of the Open Source Initiative.
 * 
 * This file is part of DrJava.  Download the current version of this project
 * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 * 
 * END_COPYRIGHT_BLOCK*/

package edu.rice.cs.javalanglevels;

import java.util.*;
import edu.rice.cs.javalanglevels.*;
import edu.rice.cs.javalanglevels.parser.*;
import edu.rice.cs.javalanglevels.tree.*;
import java.io.*;
import edu.rice.cs.plt.reflect.JavaVersion;

/**
 * This class represents the mechanism by which we convert a language level file to a .java file of the same name by
 * first visiting the file to error-check it, and then augment the file.  This class is tested at the top level in the AdvancedLevelTest,
 * ElementaryLevelTest, and IntermediateLevelTest.
 */
public class LanguageLevelConverter {
  /* For Corky's version: set this to false */
  private static final boolean SAFE_SUPPORT_CODE = false;
  
  /**Holds any parse exceptions that are encountered*/
  private LinkedList<JExprParseException> _parseExceptions = new LinkedList<JExprParseException>();
  
  /**Holds any visitor exceptions that are encountered*/
  private LinkedList<Pair<String, JExpressionIF>> _visitorErrors = new LinkedList<Pair<String, JExpressionIF>>();
  
  /**The java version of the compiler*/
  private JavaVersion _targetVersion;
  
  /**By default, let the version be 1.4.2*/
  public LanguageLevelConverter() {
    _targetVersion = JavaVersion.JAVA_1_4;
  }
  
  /**Set the target version to what is specified by the user's compiler*/
  public LanguageLevelConverter(JavaVersion targetJavaVersion) {
    _targetVersion = targetJavaVersion;
  }
  
  /***Add the parse exception to the list of parse exceptions*/
  private void _addParseException(ParseException pe) {
    JExprParseException jpe;
    if (pe instanceof JExprParseException) { jpe = (JExprParseException) pe; }
    else { jpe = new JExprParseException(pe); }
    _parseExceptions.addLast(jpe);
  }
  
  /**Add the visitor error to the list of errors*/
  private void _addVisitorError(Pair<String, JExpressionIF> ve) {
    _visitorErrors.addLast(ve);
  }
  
  /**Parse, Visit, Type Check, and Convert any language level files in the array of files*/
  public Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>> convert(File[] files) {
    LanguageLevelVisitor._newSDs=new Hashtable<SymbolData, LanguageLevelVisitor>(); /**initialize so we don't get null pointer exception*/
    // We need a LinkedList for errors to be shared by the visitors to each file.
    LinkedList<Pair<String, JExpressionIF>> languageLevelVisitorErrors = new LinkedList<Pair<String, JExpressionIF>>();
    
    //keep track of the continuations to resolve
    Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>> continuations = new Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>>();
    
    //and the newSDs we've created
    Hashtable<SymbolData, LanguageLevelVisitor> languageLevelNewSDs = new Hashtable<SymbolData, LanguageLevelVisitor>();
    
    // Similarly, we need a Hashtable for a shared symbolTable.
    Symboltable languageLevelVisitorSymbolTable = new Symboltable();

    //And a linked list thing to share visited files.
    LinkedList<Pair<LanguageLevelVisitor, SourceFile>> languageLevelVisitedFiles = new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>();
    
    // We are doing two passes on the files, and the second pass needs the first's corresponding
    // SourceFile and ElementaryVisitor so we'll keep them around in a Hashtable.
    Hashtable<Integer, Pair<SourceFile, LanguageLevelVisitor>> mediator = new Hashtable<Integer, Pair<SourceFile, LanguageLevelVisitor>>();
    
    // The visitedFiles returned by any pass may include another file which is already scheduled for compilation.
    // In this case, we don't want to reparse, or perform either the first pass or the type check since it has
    // already been done.  (An error is thrown if we do since it thinks the class has already been defined).
    LinkedList<File> filesNotToCheck = new LinkedList<File>();
    
    // The number of files to compile may change if one file references another one.
    // We don't want to visit these newly referenced files because they've already
    // been visited.
    int originalNumOfFiles = files.length;
    
    // Find the ones that are LL files.
    // Do the passes first for ALL files before proceeding to code augmentation.
    // Otherwise if one class' superclass get augmented first, then it sees a lot
    // of illegal constructs (e.g. public and constructors).
    
    //will maintain the files we visit along with their visitors (for type checking step).
    LinkedList<Pair<LanguageLevelVisitor, SourceFile>> visited = new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>();

    
    for (int ind = 0; ind < originalNumOfFiles; ind++) {
      
      try {
        BufferedReader tempBr = new BufferedReader(new FileReader(files[ind]));
        String firstLine = tempBr.readLine();
        if (firstLine == null) {
          continue;
        }
        tempBr.close();
        // If the file has the correct suffix, then parse it.
        // Also ignore files which are in filesNotToCheck.
        //for some reason, doing .contains on filesNotToCheck was not returning what we wanted.
        //So do this manually, trying to match the Absolute path.
        boolean foundFile = false;
        for (int i = 0; i<filesNotToCheck.size(); i++) {
          if (filesNotToCheck.get(i).getAbsolutePath().equals(files[ind].getAbsolutePath())) {
            foundFile = true;
            break;
          }
        }

        if (_isLanguageLevelFile(files[ind]) && !foundFile) {
          System.out.flush();
          SourceFile sf;
          File f = files[ind];
          JExprParser jep = new JExprParser(f);
          try { 
            sf = jep.SourceFile();
          }
          catch (ParseException pe) {
            // If there is a ParseException, go to next file.
            _addParseException(pe);
            continue;
          }
          
          //Now create a LanguageLevelVisitor to do the first pass over the file.
          LanguageLevelVisitor llv = null;
          if (isElementaryFile(f)) {
            llv = new ElementaryVisitor(f, new LinkedList<Pair<String, JExpressionIF>>(), languageLevelVisitorSymbolTable, new Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>>(), languageLevelVisitedFiles, languageLevelNewSDs, _targetVersion);
          }
          else if (isIntermediateFile(f)) {
            llv = new IntermediateVisitor(f, new LinkedList<Pair<String, JExpressionIF>>(), languageLevelVisitorSymbolTable, new Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>>(), languageLevelVisitedFiles, languageLevelNewSDs, _targetVersion);
          }
          else if (isAdvancedFile(f)) {
            llv = new AdvancedVisitor(f, new LinkedList<Pair<String, JExpressionIF>>(), languageLevelVisitorSymbolTable, new Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>>(), languageLevelVisitedFiles, languageLevelNewSDs, _targetVersion);
          }
          else {
            throw new RuntimeException("Internal Bug: Invalid file format not caught initially.  Please report this bug.");
          }

          
          // First pass
          sf.visit(llv);
          visited.add(new Pair<LanguageLevelVisitor, SourceFile>(llv, sf));
          //add the continuations to the hash table.
          continuations.putAll(llv.continuations);
          languageLevelVisitorErrors.addAll(llv.errors);
        }
      }
      catch (IOException ioe) {
        // The NullLiteral is a hack to get a JExpression with the correct SourceInfo inside.
        _addVisitorError(new Pair<String, JExpressionIF>(ioe.getMessage(), new NullLiteral(JExprParser.NO_SOURCE_INFO)));
      }

    }
    
    
    //Resolve continuations and create constructors.  Also accumulate errors.
    LanguageLevelVisitor.errors = new LinkedList<Pair<String, JExpressionIF>>(); //clear out error list
    
    
        //Resolve continuations
    //Hashtable<String, Pair<SourceInfo, LanguageLevelVisitor>> continuations = llv.continuations;
    
    while (!continuations.isEmpty()) {
      Enumeration<String> en = continuations.keys();
      
      while (en.hasMoreElements()) {
        String className = en.nextElement();
        Pair<SourceInfo, LanguageLevelVisitor> pair = continuations.remove(className);
        SymbolData returnedSd = pair.getSecond().getSymbolData(className, pair.getFirst(), true);
        if (returnedSd == null) {
          LanguageLevelVisitor.errors.add(new Pair<String, JExpressionIF>("Could not resolve " + className, new NullLiteral(pair.getFirst())));
        }
      }
    }
    
    //now create any constructors.
    while (!LanguageLevelVisitor._newSDs.isEmpty()) {
        Enumeration<SymbolData> en = LanguageLevelVisitor._newSDs.keys();
      while (en.hasMoreElements()) {
        SymbolData first = en.nextElement();
        LanguageLevelVisitor sdlv = LanguageLevelVisitor._newSDs.remove(first);
        sdlv.createConstructor(first);
      }
    }
    
    languageLevelVisitorErrors.addAll(LanguageLevelVisitor.errors); //add any errors that accumulated during the continuation resolving/constructor generation
    
    // At this point, there should be no continuations and visitedFiles should be completely populated.
    // Check for errors; don't type-check if there are errors.
    if (languageLevelVisitorErrors.size() > 0) {
      _visitorErrors.addAll(languageLevelVisitorErrors);
    }
    
    
    //Let's TYPE CHECK!!!
          
          else {
            
            for (int ind = 0; ind<visited.size(); ind++) {
  
              LanguageLevelVisitor llv = visited.get(ind).getFirst();
              SourceFile sf = visited.get(ind).getSecond();
              
              //TODO: This is a terrible hack to get around the following problem.  Basically, when we autobox in isAssignableTo in SymbolData, 
              //we look through the object hierarchy for the primitive.  If its corresponding boxed object isn't in the symboltable, we're in trouble.
              //So, for those special cases, go ahead and make sure the object types are in the symbol table before we start type checking.
              //I'd like to make this better, but at least it works for now.
              
              
              //Before you type check, make sure that all boxed types of primitives are in the symbol table
              if (llv.symbolTable.get("java.lang.Integer") == null) {llv.getSymbolData("java.lang.Integer", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Double")==null) {llv.getSymbolData("java.lang.Double", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Character")==null) {llv.getSymbolData("java.lang.Character", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Boolean")==null) {llv.getSymbolData("java.lang.Boolean", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Long")==null) {llv.getSymbolData("java.lang.Long", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Byte")==null) {llv.getSymbolData("java.lang.Byte", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Short")==null) {llv.getSymbolData("java.lang.Short", JExprParser.NO_SOURCE_INFO);}
              if (llv.symbolTable.get("java.lang.Float")==null) {llv.getSymbolData("java.lang.Float", JExprParser.NO_SOURCE_INFO);}
              
              
              // Type check.
              TypeChecker btc = new TypeChecker(llv._file, llv._package, llv.errors, llv.symbolTable, llv._importedFiles, llv._importedPackages, _targetVersion);
              sf.visit(btc);
              if (btc.errors.size() > 0) {
                _visitorErrors.addAll(btc.errors);
              }
              
              // Add those files to be compiled to the array of files.
              if (llv.visitedFiles.size() > 0) {            
                LinkedList<File> newFiles = new LinkedList<File>();
                for (int i = 0; i < files.length; i++) {
                  newFiles.addLast(files[i]);
                }
                Iterator<Pair<LanguageLevelVisitor, SourceFile>> iter = llv.visitedFiles.iterator();
                while (iter.hasNext()) {
                  Pair<LanguageLevelVisitor,SourceFile> currPair = iter.next();
                  File fileToAdd = currPair.getFirst()._file;
                  // if currSf is not null, then this visitedFile came from visiting the source file, not the class file. 
                  // We want to compile this source file; add it to the list of files after creating the mediator entry
                  // if necessary.
                  SourceFile currSf = currPair.getSecond();
                  if (currSf != null) {
                    if (newFiles.contains(fileToAdd)) {
                      // Messy, but we must make a mediator entry so that code augmentation has all the data it needs
                      // since we will skip visiting this file here.  
                      // Can just pass llv since it will contain the correct symbolTable.
                      mediator.put(new Integer(newFiles.indexOf(fileToAdd)), new Pair<SourceFile, LanguageLevelVisitor>(currSf, currPair.getFirst())); 
                    }
                    // Now, if we aren't already compiling this file, and we visited the source, add it to the list of
                    // files to be compiled.
                    if (!newFiles.contains(fileToAdd) && currSf != null) {
                      mediator.put(new Integer(newFiles.size()), new Pair<SourceFile, LanguageLevelVisitor>(currSf, currPair.getFirst()));
                      newFiles.addLast(fileToAdd);
                    }
                    
                  }
                  // Also make sure not to re-check these files whether we visited source or class file. 
                  // We only want to perform code augmentation since these files have already been visited.
                  if (!filesNotToCheck.contains(fileToAdd)) {
                    filesNotToCheck.addLast(fileToAdd);
                  }
                }
                files = (File[]) newFiles.toArray(new File[newFiles.size()]);
              }
              
              mediator.put(new Integer(ind), new Pair<SourceFile, LanguageLevelVisitor>(sf, llv));
            }
          }
    
    
    //If there were any errors in the llv pass or the type checking pass, just return them.
    if (_parseExceptions.size() > 0 || _visitorErrors.size() > 0) {
      return new Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>>(_parseExceptions, _visitorErrors);
    }
    
    // Now do code augmentation.
    for (int ind = 0; ind < files.length; ind++) {
      try {
        BufferedReader tempBr = new BufferedReader(new FileReader(files[ind]));
        String firstLine = tempBr.readLine();
        tempBr.close(); // Important to close the reader, otherwise Windows will not allow the renameTo call later.
        if (firstLine == null) {
          continue;
        }

        // If the file has the correct beginner tag at the beginning, then parse it.
        // It should not end in .beginner since that's just a temporary copy which gets
        // deleted after the compile.
        if (_isLanguageLevelFile(files[ind])) {
          Pair<SourceFile, LanguageLevelVisitor> pair = mediator.get(new Integer(ind));
          if (pair != null) { //if pair is null, we do not actually need to augment this file--it wasn't visited.  Maybe we used the class file?
            SourceFile sf = pair.getFirst();
            LanguageLevelVisitor llv = pair.getSecond();
            File f = files[ind];
            // Do code augmentation.  This will involve a line-by-line copy, editing
            // lines as appropriate.
            String augmentedFilePath = f.getAbsolutePath();
            augmentedFilePath = augmentedFilePath.substring(0, augmentedFilePath.length() - 4); //remove the .dj# extension
            File augmentedFile = new File(augmentedFilePath + ".java"); //replace it with .java
            BufferedReader br = new BufferedReader(new FileReader(f));
            BufferedWriter bw = new BufferedWriter(new FileWriter(augmentedFile));
            
            Augmentor a = new Augmentor(_targetVersion, SAFE_SUPPORT_CODE, br, bw, llv);
            sf.visit(a);
            
            br.close();
            bw.close();
          }
        }
      }
      catch (Augmentor.Exception ae) {
        // The NullLiteral is a hack to get a JExpression with the correct SourceInfo inside.
        _addVisitorError(new Pair<String, JExpressionIF>(ae.getMessage(), new NullLiteral(JExprParser.NO_SOURCE_INFO)));
      }
      catch (IOException ioe) {
        // The NullLiteral is a hack to get a JExpression with the correct SourceInfo inside.
        _addVisitorError(new Pair<String, JExpressionIF>(ioe.getMessage(), new NullLiteral(JExprParser.NO_SOURCE_INFO)));
      }
    }
    return new Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>>(_parseExceptions,
                                                                                       _visitorErrors);
  }
    
  /**If a file name ends with .dj0, it is an Elementary File*/
  public static boolean isElementaryFile(File f) {
    return f.getPath().endsWith(".dj0");
  }
  
  /**If a file name ends with .dj1, it is an Intermediate File*/
  public static boolean isIntermediateFile(File f) {
    return f.getPath().endsWith(".dj1");
  }
  
  /**If a file name ends with .dj2, it is an Advanced File*/
  public static boolean isAdvancedFile(File f) {
    return f.getPath().endsWith(".dj2");
  }
  
  /**If a file is an Elementary, Intermediate, or Advanced file, then it is a language level file*/
  public static boolean _isLanguageLevelFile(File f) {
    return isElementaryFile(f) || isIntermediateFile(f) || isAdvancedFile(f);
  }
  
  /**Only certain versions of the java compiler support autoboxing*/
  public static boolean versionSupportsAutoboxing(JavaVersion version) {
    return version.supports(JavaVersion.JAVA_5);
    // If we care to support it, also allows JSR-14
  }
  
  /**Only certain versions of the java compiler support generics*/
  public static boolean versionSupportsGenerics(JavaVersion version) {
    return version.supports(JavaVersion.JAVA_5);
    // If we care to support it, also allows JSR-14
  }

  /**Only 1.5 supports for each*/
  public static boolean versionSupportsForEach(JavaVersion version) {
    return version.supports(JavaVersion.JAVA_5);
  }
  
  /* @return true if the compiler version is 1.5 */
  public static boolean versionIs15(JavaVersion version) {
    return version.supports(JavaVersion.JAVA_5);
  }
  
  /**Do a conversion from the command line, to allow quick testing*/
  public static void main(String[] args) {
    LanguageLevelConverter llc = new LanguageLevelConverter(JavaVersion.CURRENT);
    File[] files = new File[args.length];
    for (int i = 0; i < args.length; i++) {
      files[i] = new File(args[i]);
    }
    
    Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>> result = 
        llc.convert(files);
    System.out.println(result.getFirst().size() + result.getSecond().size() + " errors.");
    for(JExprParseException p : result.getFirst()) {
      System.out.println(p);
    }
    for(Pair<String, JExpressionIF> p : result.getSecond()) {
      System.out.println(p.getFirst() + " " + p.getSecond().getSourceInfo());
    }
  }
  
}




See more files for this project here

DrJava

DrJava is a lightweight programming environment for Java designed to foster test-driven software development. It includes an intelligent program editor, an interactions pane for evaluating program text, a source level debugger, and a unit testing tool.

Project homepage: http://sourceforge.net/projects/drjava
Programming language(s): Java
License: other

  AdvancedLevelTest.java
  AdvancedVisitor.java
  ArrayData.java
  Augmentor.java
  BlockData.java
  Bob.java
  BodyBodyAdvancedVisitor.java
  BodyBodyElementaryVisitor.java
  BodyBodyIntermediateVisitor.java
  BodyData.java
  BodyTypeChecker.java
  CharConverter.java
  ClassBodyAdvancedVisitor.java
  ClassBodyElementaryVisitor.java
  ClassBodyIntermediateVisitor.java
  ClassBodyTypeChecker.java
  ConstructorBodyTypeChecker.java
  Data.java
  ElementaryLevelTest.java
  ElementaryVisitor.java
  ExpressionTypeChecker.java
  InstanceData.java
  InterfaceBodyAdvancedVisitor.java
  InterfaceBodyIntermediateVisitor.java
  InterfaceBodyTypeChecker.java
  IntermediateLevelTest.java
  IntermediateVisitor.java
  JExprParseException.java
  JExprParserTest.java
  JExpressionIFPrunableDepthFirstVisitor_void.java
  LValueTypeChecker.java
  LValueWithValueTypeChecker.java
  LanguageLevelConverter.java
  LanguageLevelVisitor.java
  MethodData.java
  PackageData.java
  Pair.java
  PrimitiveData.java
  SourceInfo.java
  SymbolData.java
  Symboltable.java
  TryCatchBodyTypeChecker.java
  TypeChecker.java
  TypeData.java
  VariableData.java
  VoidMethodsNotAllowedClassBodyTypeChecker.java
  jexpr.ast
  jexpr.jj