BatchEnvironment.java :  » 6.0-JDK-Modules-sun » tools » sun » tools » javac » Java Open Source

Java Open Source » 6.0 JDK Modules sun » tools 
tools » sun » tools » javac » BatchEnvironment.java
/*
 * Copyright 1994-2004 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package sun.tools.javac;

import sun.tools.java.*;
import sun.tools.tree.Node;
import sun.tools.java.Package;

import java.util.*;
import java.io.*;

/**
 * Main environment of the batch version of the Java compiler,
 * this needs more work.
 *
 * WARNING: The contents of this source file are not part of any
 * supported API.  Code that depends on them does so at its own risk:
 * they are subject to change or removal without notice.
 */
@Deprecated
public 
class BatchEnvironment extends Environment implements ErrorConsumer {
    /**
     * The stream where error message are printed.
     */
    OutputStream out;
    
    /**
     * The path we use for finding source files.
     */
    protected ClassPath sourcePath;
    
    /**
     * The path we use for finding class (binary) files.
     */
    protected ClassPath binaryPath;
    
    /**
     * A hashtable of resource contexts.
     */
    Hashtable packages = new Hashtable(31);

    /**
     * The classes, in order of appearance.
     */
    Vector classesOrdered = new Vector();

    /**
     * The classes, keyed by ClassDeclaration.
     */
    Hashtable classes = new Hashtable(351);

    /**
     * flags
     */
    public int flags;

    /**
     * Major and minor versions to use for generated class files.
     * Environments that extend BatchEnvironment (such as javadoc's
     * Env class) get the default values below.
     *
     * javac itself may override these versions with values determined
     * from the command line "-target" option.
     */
    public short majorVersion = JAVA_DEFAULT_VERSION;
    public short minorVersion = JAVA_DEFAULT_MINOR_VERSION;

// JCOV
    /**
     * coverage data file
     */
    public File covFile;
// end JCOV

    /**
     * The number of errors and warnings
     */
    public int nerrors;
    public int nwarnings;
    public int ndeprecations;

    /**
     * A list of files containing deprecation warnings.
     */
    Vector deprecationFiles = new Vector();

  /**
   * writes out error messages
   */
  
  ErrorConsumer errorConsumer;
  
    /**
     * Old constructors -- these constructors build a BatchEnvironment
     * with an old-style class path.
     */
    public BatchEnvironment(ClassPath path) {
  this(System.out, path);
    }
    public BatchEnvironment(OutputStream out,
          ClassPath path) {
      this(out, path, (ErrorConsumer) null);
    }
    public BatchEnvironment(OutputStream out,
          ClassPath path,
          ErrorConsumer errorConsumer) {
  this(out, path, path, errorConsumer);
    }

    /**
     * New constructors -- these constructors build a BatchEnvironment
     * with a source path and a binary path.
     */
    public BatchEnvironment(ClassPath sourcePath,
          ClassPath binaryPath) {
  this(System.out, sourcePath, binaryPath);
    }
    public BatchEnvironment(OutputStream out,
          ClassPath sourcePath,
          ClassPath binaryPath) {
      this(out, sourcePath, binaryPath, (ErrorConsumer) null);
    }
    public BatchEnvironment(OutputStream out,
          ClassPath sourcePath,
          ClassPath binaryPath,
          ErrorConsumer errorConsumer) {
  this.out = out;
  this.sourcePath = sourcePath;
  this.binaryPath = binaryPath;
  this.errorConsumer = (errorConsumer == null) ? this : errorConsumer;
    }

    /**
     * Factory 
     */
    static BatchEnvironment create(OutputStream out,
                                   String srcPathString, 
                                   String classPathString, 
                                   String sysClassPathString, 
                                   String extDirsString){
        ClassPath[] classPaths = classPaths(srcPathString, classPathString, 
                                            sysClassPathString, extDirsString);
        return new BatchEnvironment(out, classPaths[0], classPaths[1]);
    }
        
    protected static ClassPath[] classPaths(String srcPathString, 
                                            String classPathString, 
                                            String sysClassPathString, 
                                            String extDirsString) {
        // Create our source classpath and our binary classpath
        ClassPath sourcePath;
        ClassPath binaryPath;
        StringBuffer binaryPathBuffer = new StringBuffer();

        if (classPathString == null) {
      // The env.class.path property is the user's CLASSPATH
      // environment variable, and it set by the wrapper (ie,
      // javac.exe).
      classPathString = System.getProperty("env.class.path");
      if (classPathString == null) {
    classPathString = ".";
      }
        }
        if (srcPathString == null) {
            srcPathString = classPathString;
        }
        if (sysClassPathString == null) {
            sysClassPathString = System.getProperty("sun.boot.class.path");
      if (sysClassPathString == null) { // shouldn't happen; recover gracefully
    sysClassPathString = classPathString;
      }
  }
        appendPath(binaryPathBuffer, sysClassPathString);

        if (extDirsString == null) {
            extDirsString = System.getProperty("java.ext.dirs");
        }
        if (extDirsString != null) {
            StringTokenizer st = new StringTokenizer(extDirsString, 
                                                     File.pathSeparator);
            while (st.hasMoreTokens()) {
                String dirName = st.nextToken();
                File dir = new File(dirName);
                if (!dirName.endsWith(File.separator)) {
                    dirName += File.separator;
                }
                if (dir.isDirectory()) {
                    String[] files = dir.list();
                    for (int i = 0; i < files.length; ++i) {
                        String name = files[i];
                        if (name.endsWith(".jar")) {
                            appendPath(binaryPathBuffer, dirName + name);
                        }
                    }
                }
            }
        }
        
        appendPath(binaryPathBuffer, classPathString);

        sourcePath = new ClassPath(srcPathString);
        binaryPath = new ClassPath(binaryPathBuffer.toString());

        return new ClassPath[]{sourcePath, binaryPath};
    }

    private static void appendPath(StringBuffer buf, String str) {
        if (str.length() > 0) {
            if (buf.length() > 0) {
                buf.append(File.pathSeparator);
            }
            buf.append(str);
        }
    }

    /**
     * Return flags
     */
    public int getFlags() {
        return flags;
    }

    /**
     * Return major version to use for generated class files
     */
    public short getMajorVersion() {
        return majorVersion;
    }

    /**
     * Return minor version to use for generated class files
     */
    public short getMinorVersion() {
        return minorVersion;
    }

// JCOV
    /**
     * Return coverage data file
     */
    public File getcovFile() {
        return covFile;
    }
// end JCOV

    /**
     * Return an enumeration of all the currently defined classes
     * in order of appearance to getClassDeclaration().
     */
    public Enumeration getClasses() {
  return classesOrdered.elements();
    }

    /**
     * A set of Identifiers for all packages exempt from the "exists"
     * check in Imports#resolve().  These are the current packages for
     * all classes being compiled as of the first call to isExemptPackage.
     */
    private Set exemptPackages;

    /**
     * Tells whether an Identifier refers to a package which should be
     * exempt from the "exists" check in Imports#resolve().
     */
    public boolean isExemptPackage(Identifier id) {
  if (exemptPackages == null) {
      // Collect a list of the packages of all classes currently
      // being compiled.
      setExemptPackages();
  }

  return exemptPackages.contains(id);
    }

    /**
     * Set the set of packages which are exempt from the exists check
     * in Imports#resolve().
     */
    private void setExemptPackages() {
  // The JLS gives us the freedom to define "accessibility" of
  // a package in whatever manner we wish.  After the evaluation
  // of bug 4093217, we have decided to consider a package P
  // accessible if either:
  //
  // 1. The directory corresponding to P exists on the classpath.
  // 2. For any class C currently being compiled, C belongs to
  //    package P.
  // 3. For any class C currently being compiled, C belongs to
  //    package Q and Q is a subpackage of P.
  //
  // In order to implement this, we collect the current packages
  // (and prefixes) of all packages we have found so far.  These
  // will be exempt from the "exists" check in
  // sun.tools.java.Imports#resolve().

  exemptPackages = new HashSet(101);

  // Add all of the current packages and their prefixes to our set.
  for (Enumeration e = getClasses(); e.hasMoreElements(); ) {
      ClassDeclaration c = (ClassDeclaration) e.nextElement();
      if (c.getStatus() == CS_PARSED) {
    SourceClass def = (SourceClass) c.getClassDefinition();
    if (def.isLocal())
        continue;
    
    Identifier pkg = def.getImports().getCurrentPackage();
    
    // Add the name of this package and all of its prefixes
    // to our set.
    while (pkg != idNull && exemptPackages.add(pkg)) {
        pkg = pkg.getQualifier();
    }
      }
  }
  
  // Before we go any further, we make sure java.lang is
  // accessible and that it is not ambiguous.  These checks
  // are performed for "ordinary" packages in
  // sun.tools.java.Imports#resolve().  The reason we perform
  // them specially for java.lang is that we want to report
  // the error once, and outside of any particular file.

  // Check to see if java.lang is accessible.
  if (!exemptPackages.contains(idJavaLang)) {
      // Add java.lang to the set of exempt packages.
      exemptPackages.add(idJavaLang);

      try {
    if (!getPackage(idJavaLang).exists()) {
        // java.lang doesn't exist.
        error(0, "package.not.found.strong", idJavaLang);
        return;
    }
      } catch (IOException ee) {
    // We got an IO exception checking to see if the package
    // java.lang exists.
    error(0, "io.exception.package", idJavaLang);
      }
  }

  // Next we ensure that java.lang is not both a class and
  // a package.  (Fix for 4101529)
  //
  // This change has been backed out because, on WIN32, it
  // failed to take character case into account.  It will
  // be put back in later.
  //
  // Identifier resolvedName =
  //   resolvePackageQualifiedName(idJavaLang);
  // Identifier topClassName = resolvedName.getTopName();
  //     //if (Imports.importable(topClassName, env)) {
  // if (Imports.importable(topClassName, this)) {
  //    // It is a package and a class.  Emit the error.
  //    error(0, "package.class.conflict.strong",
  //        idJavaLang, topClassName);
  //    return;
  // }
    }

    /**
     * Get a class, given the fully qualified class name
     */
    public ClassDeclaration getClassDeclaration(Identifier nm) {
  return getClassDeclaration(Type.tClass(nm));
    }

    public ClassDeclaration getClassDeclaration(Type t) {
  ClassDeclaration c = (ClassDeclaration)classes.get(t);
  if (c == null) {
      classes.put(t, c = new ClassDeclaration(t.getClassName()));
      classesOrdered.addElement(c);
  }
  return c;
    }

    /**
     * Check if a class exists
     * Applies only to package members (non-nested classes).
     */
    public boolean classExists(Identifier nm) {
  if (nm.isInner()) {
      nm = nm.getTopName();  // just in case
  }
  Type t = Type.tClass(nm);
  try {
      ClassDeclaration c = (ClassDeclaration)classes.get(t);
      return (c != null) ? c.getName().equals(nm) :
    getPackage(nm.getQualifier()).classExists(nm.getName());
  } catch (IOException e) {
      return true;
  }
    }

    /**
     * Generate a new name similar to the given one.
     * Do it in such a way that repeated compilations of
     * the same source generate the same series of names.
     */

    // This code does not perform as stated above.
    // Correction below is part of fix for bug id 4056065.
    //
    // NOTE: The method 'generateName' has now been folded into its
    // single caller, 'makeClassDefinition', which appears later in
    // this file.

    /*--------------------------*
    public Identifier generateName(ClassDefinition outerClass, Identifier nm) {
  Identifier outerNm = outerClass.getName();
  Identifier flat = outerNm.getFlatName();
  Identifier stem = Identifier.lookup(outerNm.getQualifier(),
              flat.getHead());
  for (int i = 1; ; i++) {
      String name = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm);
      Identifier nm1 = Identifier.lookupInner(stem,
                Identifier.lookup(name));
      if (classes.get(Type.tClass(nm1)) == null)
    return nm1;
  }
    }
    *--------------------------*/

    /**
     * Get the package path for a package
     */
    public Package getPackage(Identifier pkg) throws IOException {
  Package p = (Package)packages.get(pkg);
  if (p == null) {
      packages.put(pkg, p = new Package(sourcePath, binaryPath, pkg));
  }
  return p;
    }
    
    /**
     * Parse a source file
     */
    public void parseFile(ClassFile file) throws FileNotFoundException {
  long tm = System.currentTimeMillis();
  InputStream input;
  BatchParser p;
  
  if (tracing) dtEnter("parseFile: PARSING SOURCE " + file);

  Environment env = new Environment(this, file);

  try {
      input = file.getInputStream();
      env.setCharacterEncoding(getCharacterEncoding());
      //      p = new BatchParser(e, new BufferedInputStream(input));
      p = new BatchParser(env, input);
  } catch(IOException ex) {
      if (tracing) dtEvent("parseFile: IO EXCEPTION " + file);
      throw new FileNotFoundException();
  }

  try {
      p.parseFile();
  } catch(Exception e) {
      throw new CompilerError(e);
  }

  try {  
      input.close();
  } catch (IOException ex) {
      // We're turn with the input, so ignore this.
  }

  if (verbose()) {
      tm = System.currentTimeMillis() - tm;
      output(Main.getText("benv.parsed_in", file.getPath(),
        Long.toString(tm)));
  }

  if (p.classes.size() == 0) {
      // The JLS allows a file to contain no compilation units --
      // that is, it allows a file to contain no classes or interfaces.
      // In this case, we are still responsible for checking that the
      // imports resolve properly.  The way the compiler is organized,
      // this is the last point at which we still have enough information
      // to do so. (Fix for 4041851).
      p.imports.resolve(env);
  } else {
      // In an attempt to see that classes which come from the
      // same source file are all recompiled when any one of them
      // would be recompiled (when using the -depend option) we
      // introduce artificial dependencies between these classes.
      // We do this by calling the addDependency() method, which
      // adds a (potentially unused) class reference to the constant
      // pool of the class.
      //
      // Previously, we added a dependency from every class in the
      // file, to every class in the file.  This introduced, in
      // total, a quadratic number of potentially bogus constant
      // pool entries.  This was bad.  Now we add our artificial
      // dependencies in such a way that the classes are connected
      // in a circle.  While single links is probably sufficient, the
      // code below adds double links just to be diligent.
      // (Fix for 4108286).
      // 
      // Note that we don't chain in inner classes.  The links
      // between them and their outerclass should be sufficient
      // here.
      // (Fix for 4107960).
      //
      // The dependency code was previously in BatchParser.java.
      Enumeration e = p.classes.elements();

      // first will not be an inner class.
      ClassDefinition first = (ClassDefinition) e.nextElement();
      if (first.isInnerClass()) {
    throw new CompilerError("BatchEnvironment, first is inner");
      }

      ClassDefinition current = first;
      ClassDefinition next;
      while (e.hasMoreElements()) {
    next = (ClassDefinition) e.nextElement();
    // Don't chain in inner classes.
    if (next.isInnerClass()) {
        continue;
    }
    current.addDependency(next.getClassDeclaration());
    next.addDependency(current.getClassDeclaration());
    current = next;
      }
      // Make a circle.  Don't bother to add a dependency if there
      // is only one class in the file.
      if (current != first) {
    current.addDependency(first.getClassDeclaration());
    first.addDependency(current.getClassDeclaration());
      }
  }

  if (tracing) dtExit("parseFile: SOURCE PARSED " + file);
    }

    /**
     * Load a binary file
     */
    BinaryClass loadFile(ClassFile file) throws IOException {
  long tm = System.currentTimeMillis();
  InputStream input = file.getInputStream();
  BinaryClass c = null;

  if (tracing) dtEnter("loadFile: LOADING CLASSFILE " + file);

  try {
      DataInputStream is = 
    new DataInputStream(new BufferedInputStream(input));
      c = BinaryClass.load(new Environment(this, file), is, 
         loadFileFlags());
  } catch (ClassFormatError e) {
      error(0, "class.format", file.getPath(), e.getMessage());
      if (tracing) dtExit("loadFile: CLASS FORMAT ERROR " + file);
      return null;
  } catch (java.io.EOFException e) {
      // If we get an EOF while processing a class file, then
      // it has been truncated.  We let other I/O errors pass
      // through.  Fix for 4088443.
      error(0, "truncated.class", file.getPath());
      return null;
  }

  input.close();
  if (verbose()) {
      tm = System.currentTimeMillis() - tm;
      output(Main.getText("benv.loaded_in", file.getPath(),
        Long.toString(tm)));
  }

  if (tracing) dtExit("loadFile: CLASSFILE LOADED " + file);

  return c;
    }
    
    /**
     * Default flags for loadFile.  Subclasses may override this.
     */
    int loadFileFlags() { 
  return 0; 
    }

    /**
     * Load a binary class
     */
    boolean needsCompilation(Hashtable check, ClassDeclaration c) {
  switch (c.getStatus()) {

    case CS_UNDEFINED:
      if (tracing) dtEnter("needsCompilation: UNDEFINED " + c.getName());
      loadDefinition(c);
      return needsCompilation(check, c);

    case CS_UNDECIDED:
            if (tracing) dtEnter("needsCompilation: UNDECIDED " + c.getName());
      if (check.get(c) == null) {
    check.put(c, c);

    BinaryClass bin = (BinaryClass)c.getClassDefinition();
    for (Enumeration e = bin.getDependencies() ; e.hasMoreElements() ;) {
        ClassDeclaration dep = (ClassDeclaration)e.nextElement();
        if (needsCompilation(check, dep)) {
      // It must be source, dependencies need compilation
      c.setDefinition(bin, CS_SOURCE);
      if (tracing) dtExit("needsCompilation: YES (source) " + c.getName());
      return true;
        }
    }
      }
      if (tracing) dtExit("needsCompilation: NO (undecided) " + c.getName());
      return false;

    case CS_BINARY:
      if (tracing) {
    dtEnter("needsCompilation: BINARY " + c.getName());
    dtExit("needsCompilation: NO (binary) " + c.getName());
      }
      return false;

  }

  if (tracing) dtExit("needsCompilation: YES " + c.getName());
  return true;
    }

    /**
     * Load the definition of a class
     * or at least determine how to load it.
     * The caller must repeat calls to this method
     * until it the state converges to CS_BINARY, CS_PARSED, or the like..
     * @see ClassDeclaration#getClassDefinition
     */
    public void loadDefinition(ClassDeclaration c) {
  if (tracing) dtEnter("loadDefinition: ENTER " +
           c.getName() + ", status " + c.getStatus());
  switch (c.getStatus()) {
    case CS_UNDEFINED: {
      if (tracing)
    dtEvent("loadDefinition: STATUS IS UNDEFINED");
      Identifier nm = c.getName();
      Package pkg;
      try {
    pkg = getPackage(nm.getQualifier());
      } catch (IOException e) {
    // If we can't get at the package, then we'll just
    // have to set the class to be not found.
    c.setDefinition(null, CS_NOTFOUND);

    error(0, "io.exception", c);
    if (tracing)
        dtExit("loadDefinition: IO EXCEPTION (package)");
    return;
      }
      ClassFile binfile = pkg.getBinaryFile(nm.getName());
      if (binfile == null) {
    // must be source, there is no binary
    c.setDefinition(null, CS_SOURCE);
    if (tracing)
        dtExit("loadDefinition: MUST BE SOURCE (no binary) " +
         c.getName());
    return;
      }

      ClassFile srcfile = pkg.getSourceFile(nm.getName());
      if (srcfile == null) {
    if (tracing)
        dtEvent("loadDefinition: NO SOURCE " + c.getName());
    BinaryClass bc = null;
    try {
        bc = loadFile(binfile);
    } catch (IOException e) {
        // If we can't access the binary, set the class to
        // be not found.  (bug id 4030497)
        c.setDefinition(null, CS_NOTFOUND);

        error(0, "io.exception", binfile);
        if (tracing)
      dtExit("loadDefinition: IO EXCEPTION (binary)");
        return;
    }
    if ((bc != null) && !bc.getName().equals(nm)) {
        error(0, "wrong.class", binfile.getPath(), c, bc);
        bc = null;
        if (tracing)
      dtEvent("loadDefinition: WRONG CLASS (binary)");
    }
    if (bc == null) {
        // no source nor binary found
        c.setDefinition(null, CS_NOTFOUND);
        if (tracing)
      dtExit("loadDefinition: NOT FOUND (source or binary)");
        return;
    }

    // Couldn't find the source, try the one mentioned in the binary
    if (bc.getSource() != null) {
        srcfile = new ClassFile(new File((String)bc.getSource()));
        // Look for the source file
        srcfile = pkg.getSourceFile(srcfile.getName());
        if ((srcfile != null) && srcfile.exists()) {
      if (tracing)
          dtEvent("loadDefinition: FILENAME IN BINARY " +
            srcfile);
      if (srcfile.lastModified() > binfile.lastModified()) {
          // must be source, it is newer than the binary
          c.setDefinition(bc, CS_SOURCE);
          if (tracing)
        dtEvent("loadDefinition: SOURCE IS NEWER " +
          srcfile);
          bc.loadNested(this);
          if (tracing)
        dtExit("loadDefinition: MUST BE SOURCE " +
               c.getName());
          return;
      }
      if (dependencies()) {
          c.setDefinition(bc, CS_UNDECIDED);
          if (tracing)
        dtEvent("loadDefinition: UNDECIDED " +
          c.getName());
      } else {
          c.setDefinition(bc, CS_BINARY);
          if (tracing)
        dtEvent("loadDefinition: MUST BE BINARY " +
          c.getName());
      }
      bc.loadNested(this);
      if (tracing)
          dtExit("loadDefinition: EXIT " +
           c.getName() + ", status " + c.getStatus());
      return;
        }
    } 

    // It must be binary, there is no source
    c.setDefinition(bc, CS_BINARY);
    if (tracing)
        dtEvent("loadDefinition: MUST BE BINARY (no source) " +
             c.getName());
    bc.loadNested(this);
    if (tracing)
        dtExit("loadDefinition: EXIT " +
         c.getName() + ", status " + c.getStatus());
    return;
      }
      BinaryClass bc = null;
      try {
    if (srcfile.lastModified() > binfile.lastModified()) {
        // must be source, it is newer than the binary
        c.setDefinition(null, CS_SOURCE);
        if (tracing)
      dtEvent("loadDefinition: MUST BE SOURCE (younger than binary) " +
        c.getName());
        return;
    }
    bc = loadFile(binfile);
      } catch (IOException e) {
    error(0, "io.exception", binfile);
    if (tracing)
        dtEvent("loadDefinition: IO EXCEPTION (binary)");
      }
      if ((bc != null) && !bc.getName().equals(nm)) {
    error(0, "wrong.class", binfile.getPath(), c, bc);
    bc = null;
    if (tracing)
        dtEvent("loadDefinition: WRONG CLASS (binary)");
      }
      if (bc != null) {
    Identifier name = bc.getName();
    if (name.equals(c.getName())) {
        if (dependencies()) {
      c.setDefinition(bc, CS_UNDECIDED);
      if (tracing)
          dtEvent("loadDefinition: UNDECIDED " + name);
        } else {
      c.setDefinition(bc, CS_BINARY);
      if (tracing)
          dtEvent("loadDefinition: MUST BE BINARY " + name);
        }
    } else {
        c.setDefinition(null, CS_NOTFOUND);
        if (tracing)
      dtEvent("loadDefinition: NOT FOUND (source or binary)");
        if (dependencies()) {
      getClassDeclaration(name).setDefinition(bc, CS_UNDECIDED);
      if (tracing)
          dtEvent("loadDefinition: UNDECIDED " + name);
        } else {
      getClassDeclaration(name).setDefinition(bc, CS_BINARY);
      if (tracing)
          dtEvent("loadDefinition: MUST BE BINARY " + name);
        }
    }
      } else {
    c.setDefinition(null, CS_NOTFOUND);
    if (tracing)
        dtEvent("loadDefinition: NOT FOUND (source or binary)");
      }
      if (bc != null && bc == c.getClassDefinition())
    bc.loadNested(this);
      if (tracing) dtExit("loadDefinition: EXIT " +
        c.getName() + ", status " + c.getStatus());
      return;
    }
      
    case CS_UNDECIDED: {
      if (tracing) dtEvent("loadDefinition: STATUS IS UNDECIDED");
      Hashtable tab = new Hashtable();
      if (!needsCompilation(tab, c)) {
    // All undecided classes that this class depends on must be binary
    for (Enumeration e = tab.keys() ; e.hasMoreElements() ; ) {
        ClassDeclaration dep = (ClassDeclaration)e.nextElement();
        if (dep.getStatus() == CS_UNDECIDED) {
      // must be binary, dependencies need compilation
      dep.setDefinition(dep.getClassDefinition(), CS_BINARY);
      if (tracing)
          dtEvent("loadDefinition: MUST BE BINARY " + dep);
        }
    }
      }
      if (tracing) dtExit("loadDefinition: EXIT " +
        c.getName() + ", status " + c.getStatus());
      return;
    }

    case CS_SOURCE: {
      if (tracing) dtEvent("loadDefinition: STATUS IS SOURCE");
      ClassFile srcfile = null;
      Package pkg = null;
      if (c.getClassDefinition() != null) {
    // Use the source file name from the binary class file
    try {
        pkg = getPackage(c.getName().getQualifier());
        srcfile = pkg.getSourceFile((String)c.getClassDefinition().getSource());
    } catch (IOException e) {
        error(0, "io.exception", c);
        if (tracing)
      dtEvent("loadDefinition: IO EXCEPTION (package)");
    }
    if (srcfile == null) {
        String fn = (String)c.getClassDefinition().getSource();
        srcfile = new ClassFile(new File(fn));
    }
      } else {
    // Get a source file name from the package
    Identifier nm = c.getName();
    try {
        pkg = getPackage(nm.getQualifier());
        srcfile = pkg.getSourceFile(nm.getName());
    } catch (IOException e)  {
        error(0, "io.exception", c);
        if (tracing)
      dtEvent("loadDefinition: IO EXCEPTION (package)");
    }
    if (srcfile == null) {
        // not found, there is no source
        c.setDefinition(null, CS_NOTFOUND);
        if (tracing)
      dtExit("loadDefinition: SOURCE NOT FOUND " +
             c.getName() + ", status " + c.getStatus());
        return;
    }
      }
      try {
    parseFile(srcfile);
      } catch (FileNotFoundException e) {
    error(0, "io.exception", srcfile);
    if (tracing) dtEvent("loadDefinition: IO EXCEPTION (source)");
      }
      if ((c.getClassDefinition() == null) || (c.getStatus() == CS_SOURCE)) {
    // not found after parsing the file
    error(0, "wrong.source", srcfile.getPath(), c, pkg);
    c.setDefinition(null, CS_NOTFOUND);
    if (tracing)
        dtEvent("loadDefinition: WRONG CLASS (source) " +
          c.getName());
      }
      if (tracing) dtExit("loadDefinition: EXIT " +
        c.getName() + ", status " + c.getStatus());
      return;
    }
  }
  if (tracing) dtExit("loadDefinition: EXIT " +
          c.getName() + ", status " + c.getStatus());
    }

    /**
     * Create a new class.
     */
    public ClassDefinition makeClassDefinition(Environment toplevelEnv,
                 long where,
                 IdentifierToken name,
                 String doc, int modifiers,
                 IdentifierToken superClass,
                 IdentifierToken interfaces[],
                 ClassDefinition outerClass) {

  Identifier nm = name.getName();
  long nmpos = name.getWhere();

  Identifier pkgNm;
  String mangledName = null;
  ClassDefinition localContextClass = null;

  // Provide name for a local class.  This used to be set after
  // the class was created, but it is needed for checking within
  // the class constructor.
  // NOTE: It seems that we could always provide the simple name,
  // and thereby avoid the test in 'ClassDefinition.getLocalName()'
  // for the definedness of the local name.  There, if the local
  // name is not set, a simple name is extracted from the result of
  // 'getName()'.  That name can potentially change, however, as
  // it is ultimately derived from 'ClassType.className', which is
  // set by 'Type.changeClassName'.  Better leave this alone...
  Identifier localName = null;

  if (nm.isQualified() || nm.isInner()) {
      pkgNm = nm;
  } else if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) {
      // Inaccessible class.  Create a name of the form
      // 'PackageMember.N$localName' or 'PackageMember.N'.
      // Note that the '.' will be converted later to a '$'.
      //   pkgNm = generateName(outerClass, nm);
      localContextClass = outerClass.getTopClass();
      // Always use the smallest number in generating the name that
      // renders the complete name unique within the top-level class.
      // This is required to make the names more predictable, as part
      // of a serialization-related workaround, and satisfies an obscure
      // requirement that the name of a local class be of the form
      // 'PackageMember$1$localName' when this name is unique.
      for (int i = 1 ; ; i++) {
    mangledName = i + (nm.equals(idNull) ? "" : SIG_INNERCLASS + nm);
    if (localContextClass.getLocalClass(mangledName) == null) {
        break;
    }
      }
      Identifier outerNm = localContextClass.getName();
      pkgNm = Identifier.lookupInner(outerNm, Identifier.lookup(mangledName));
      //System.out.println("LOCAL CLASS: " + pkgNm + " IN " + localContextClass);
      if ((modifiers & M_ANONYMOUS) != 0) {
    localName = idNull;
      } else {
    // Local class has a locally-scoped name which is independent of pkgNm.
    localName = nm;
      }
  } else if (outerClass != null) {
      // Accessible inner class.  Qualify name with surrounding class name.
      pkgNm = Identifier.lookupInner(outerClass.getName(), nm);
  } else {
      pkgNm = nm;
  }

  // Find the class
  ClassDeclaration c = toplevelEnv.getClassDeclaration(pkgNm);

  // Make sure this is the first definition
  if (c.isDefined()) {
      toplevelEnv.error(nmpos, "class.multidef",
            c.getName(), c.getClassDefinition().getSource());
      // Don't mess with the existing class declarations with same name
      c = new ClassDeclaration (pkgNm);
  }

  if (superClass == null && !pkgNm.equals(idJavaLangObject)) {
      superClass = new IdentifierToken(idJavaLangObject);
  }

  ClassDefinition sourceClass =
      new SourceClass(toplevelEnv, where, c, doc,
          modifiers, superClass, interfaces,
          (SourceClass) outerClass, localName);

  if (outerClass != null) {
      // It is a member of its enclosing class.
      outerClass.addMember(toplevelEnv, new SourceMember(sourceClass));
      // Record local (or anonymous) class in the class whose name will
      // serve as the prefix of the local class name.  This is necessary
      // so that the class may be retrieved from its name, which does not
      // fully represent the class nesting structure.
      // See 'ClassDefinition.getClassDefinition'.
      // This is part of a fix for bugid 4054523 and 4030421.
      if ((modifiers & (M_LOCAL | M_ANONYMOUS)) != 0) {
    localContextClass.addLocalClass(sourceClass, mangledName);
      }
  }
  
  // The local name of an anonymous or local class used to be set here
  // with a call to 'setLocalName'.  This has been moved to the constructor
  // for 'SourceClass', which now takes a 'localName' argument.

  return sourceClass;
    }

    /**
     * Create a new field.
     */
    public MemberDefinition makeMemberDefinition(Environment origEnv, long where,
                 ClassDefinition clazz,
                 String doc, int modifiers,
                 Type type, Identifier name,
                 IdentifierToken argNames[],
                 IdentifierToken expIds[],
                 Object value) {
  if (tracing) dtEvent("makeMemberDefinition: " + name + " IN " + clazz);
  Vector v = null;
  if (argNames != null) {
      v = new Vector(argNames.length);
      for (int i = 0 ; i < argNames.length ; i++) {
    v.addElement(argNames[i]);
      }
  }
  SourceMember f = new SourceMember(where, clazz, doc, modifiers,
          type, name, v, expIds, (Node)value);
  clazz.addMember(origEnv, f);
  return f;
    }

    /**
     * Release resources in classpath.
     */
    public void shutdown() {
  try {
      if (sourcePath != null) {
    sourcePath.close();
      }
      if (binaryPath != null && binaryPath != sourcePath) {
    binaryPath.close();
      }
  } catch (IOException ee) {
      output(Main.getText("benv.failed_to_close_class_path",
        ee.toString()));
  }
  sourcePath = null;
  binaryPath = null;

  super.shutdown();
    }

    /**
     * Error String
     */
    public
    String errorString(String err, Object arg1, Object arg2, Object arg3) {
        String key = null;

        if(err.startsWith("warn."))
            key = "javac.err." + err.substring(5);
        else
            key = "javac.err." + err;

  return Main.getText(key,
          arg1 != null ? arg1.toString() : null,
          arg2 != null ? arg2.toString() : null,
          arg3 != null ? arg3.toString() : null);
    }

    /**
     * The filename where the last errors have occurred
     */
    String errorFileName;

    /**
     * List of outstanding error messages
     */
    ErrorMessage errors;

    /**
     * Insert an error message in the list of outstanding error messages.
     * The list is sorted on input position and contains no duplicates.
     * The return value indicates whether or not the message was
     * actually inserted.
     *
     * The method flushErrors() used to check for duplicate error messages.
     * It would only detect duplicates if they were contiguous.  Removing
     * non-contiguous duplicate error messages is slightly less complicated
     * at insertion time, so the functionality was moved here.  This also
     * saves a miniscule number of allocations.
     */
    protected
    boolean insertError(long where, String message) {
  //output("ERR = " + message);

  if (errors == null
      ||  errors.where > where) {
      // If the list is empty, or the error comes before any other
      // errors, insert it at the beginning of the list.
      ErrorMessage newMsg = new ErrorMessage(where, message);
      newMsg.next = errors;
      errors = newMsg;

  } else if (errors.where == where
       && errors.message.equals(message)) {
      // The new message is an exact duplicate of the first message
      // in the list.  Don't insert it.
      return false;

  } else {
      // Okay, we know that the error doesn't come first.  Walk
      // the list until we find the right position for insertion.
      ErrorMessage current = errors;
      ErrorMessage next;

      while ((next = current.next) != null
       && next.where < where) {
    current = next;
      }
      
      // Now walk over any errors with the same location, looking
      // for duplicates.  If we find a duplicate, don't insert the
      // error.
      while ((next = current.next) != null
       && next.where == where) {
    if (next.message.equals(message)) {
        // We have found an exact duplicate.  Don't bother to
        // insert the error.
        return false;
    }
    current = next;
      }

      // Now insert after current.
      ErrorMessage newMsg = new ErrorMessage(where, message);
      newMsg.next = current.next;
      current.next = newMsg;
  }

  // Indicate that the insertion occurred.
  return true;
    }

    private int errorsPushed;

    /**
     * Maximum number of errors to print.
     */
    public int errorLimit = 100;

    private boolean hitErrorLimit;

    /**
     * Flush outstanding errors
     */

  public void pushError(String errorFileName, int line, String message,
            String referenceText, String referenceTextPointer) {
    int limit = errorLimit + nwarnings;
    if (++errorsPushed >= limit && errorLimit >= 0) {
        if (!hitErrorLimit) {
      hitErrorLimit = true;
      output(errorString("too.many.errors",
             new Integer(errorLimit),null,null));
        }
        return;
    }
    if (errorFileName.endsWith(".java")) {
        output(errorFileName + ":" + line + ": " + message);
        output(referenceText);
        output(referenceTextPointer);
    } else {
              // It wasn't really a source file (probably an error or
        // warning because of a malformed or badly versioned
        // class file.
        output(errorFileName + ": " + message);
    }
  }

    public void flushErrors() {
  if (errors == null) {
      return;
  }
  
  boolean inputAvail = false;
  // Read the file
  char data[] = null;
  int dataLength = 0;
  // A malformed file encoding could cause a CharConversionException.
  // If something bad happens while trying to find the source file,
  // don't bother trying to show lines.
  try {
      FileInputStream in = new FileInputStream(errorFileName);
      data = new char[in.available()];
      InputStreamReader reader =
    (getCharacterEncoding() != null ?
     new InputStreamReader(in, getCharacterEncoding()) :
     new InputStreamReader(in));
      dataLength = reader.read(data);
      reader.close();
      inputAvail = true;
  } catch(IOException e) { 
      // inputAvail will not be set
  }

  // Report the errors
  for (ErrorMessage msg = errors ; msg != null ; msg = msg.next) {
      // There used to be code here which checked
      // for duplicate error messages.  This functionality
      // has been moved to the method insertError().  See
      // the comments on that method for more information.

      int ln = (int) (msg.where >>> WHEREOFFSETBITS);
      int off = (int) (msg.where & ((1L << WHEREOFFSETBITS) - 1));
      if (off > dataLength)  off = dataLength;

      String referenceString = "";
      String markerString = "";
      if(inputAvail) {
    int i, j;
    for (i = off ; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r') ; i--);
    for (j = off ; (j < dataLength) && (data[j] != '\n') && (data[j] != '\r') ; j++);
    referenceString = new String(data, i, j - i);
      
    char strdata[] = new char[(off - i) + 1];
    for (j = i ; j < off ; j++) {
        strdata[j-i] = (data[j] == '\t') ? '\t' : ' ';
    }
    strdata[off-i] = '^';
    markerString = new String(strdata);
      }

      errorConsumer.pushError(errorFileName, ln, msg.message, 
          referenceString, markerString);
  }
  errors = null;
    }

    /**
     * Report error
     */
    public
    void reportError(Object src, long where, String err, String msg) {
  if (src == null) {
      if (errorFileName != null) {
    flushErrors();
    errorFileName = null;
      }
      if (err.startsWith("warn.")) {
    if (warnings()) {
        nwarnings++;
        output(msg);
    }
    return;
      }
      output("error: " + msg);
      nerrors++;
      flags |= F_ERRORSREPORTED;

  } else if (src instanceof String) {
      String fileName = (String)src;

      // Flush errors if we've moved on to a new file.
      if (!fileName.equals(errorFileName)) {
    flushErrors();
    errorFileName = fileName;
      }

      // Classify `err' as a warning, deprecation warning, or
      // error message.  Proceed accordingly.
      if (err.startsWith("warn.")) {
    if (err.indexOf("is.deprecated") >= 0) {
        // This is a deprecation warning.  Add `src' to the
        // list of files with deprecation warnings.
        if (!deprecationFiles.contains(src)) {
      deprecationFiles.addElement(src);
        }
        
        // If we are reporting deprecations, try to add it
        // to our list.  Otherwise, just increment the
        // deprecation count.
        if (deprecation()) {
      if (insertError(where, msg)) {
          ndeprecations++;
      }
        } else {
      ndeprecations++;
        }
    } else {
        // This is a regular warning.  If we are reporting
        // warnings, try to add it to the list.  Otherwise, just
        // increment the warning count.
        if (warnings()) {
      if (insertError(where, msg)) {
          nwarnings++;
      }
        } else {
      nwarnings++;
        }
    }
      } else {
    // This is an error.  Try to add it to the list of errors.
    // If it isn't a duplicate, increment our error count.
    if (insertError(where, msg)) {
        nerrors++;
        flags |= F_ERRORSREPORTED;
    }
      }
  } else if (src instanceof ClassFile) {
      reportError(((ClassFile)src).getPath(), where, err, msg);

  } else if (src instanceof Identifier) {
      reportError(src.toString(), where, err, msg);

  } else if (src instanceof ClassDeclaration) {
      try {
    reportError(((ClassDeclaration)src).getClassDefinition(this), where, err, msg);
      } catch (ClassNotFound e) {
    reportError(((ClassDeclaration)src).getName(), where, err, msg);
      }
  } else if (src instanceof ClassDefinition) {
      ClassDefinition c = (ClassDefinition)src;
      if (!err.startsWith("warn.")) {
    c.setError();
      } 
      reportError(c.getSource(), where, err, msg);

  } else if (src instanceof MemberDefinition) {
      reportError(((MemberDefinition)src).getClassDeclaration(), where, err, msg);

  } else {
      output(src + ":error=" + err + ":" + msg);
  }
    }

    /**
     * Issue an error
     */
    public void error(Object source, long where, String err, Object arg1, Object arg2, Object arg3) {
  if (errorsPushed >= errorLimit + nwarnings) {
      // Don't bother to queue any more errors if they won't get printed.
      return;
  }
  if (System.getProperty("javac.dump.stack") != null) {
      output("javac.err."+err+": "+errorString(err, arg1, arg2, arg3));
      new Exception("Stack trace").printStackTrace(new PrintStream(out));
  }
  reportError(source, where, err, errorString(err, arg1, arg2, arg3));
    }

    /**
     * Output a string. This can either be an error message or something
     * for debugging.
     */
    public void output(String msg) {
  PrintStream out =
      this.out instanceof PrintStream ? (PrintStream)this.out
              : new PrintStream(this.out, true);
  out.println(msg);
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.