Logo Search packages:      
Sourcecode: aspectj version File versions

AjState.java

/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Eclipse Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/epl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 *     Andy Clement     overhauled
 * ******************************************************************/

package org.aspectj.ajdt.internal.core.builder;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.aspectj.ajdt.internal.compiler.CompilationResultDestinationManager;
import org.aspectj.ajdt.internal.compiler.InterimCompilationResult;
import org.aspectj.asm.AsmManager;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.Message;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryField;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.aspectj.org.eclipse.jdt.internal.core.builder.ReferenceCollection;
import org.aspectj.org.eclipse.jdt.internal.core.builder.StringSet;
import org.aspectj.util.FileUtil;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.bcel.BcelWeaver;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.bcel.UnwovenClassFile;

/**
 * Maintains state needed for incremental compilation
 */
00063 public class AjState implements CompilerConfigurationChangeFlags {

      // SECRETAPI configures whether we use state instead of lastModTime - see pr245566
      public static boolean CHECK_STATE_FIRST = true;

      // SECRETAPI static so beware of multi-threading bugs...
      public static IStateListener stateListener = null;

      public static boolean FORCE_INCREMENTAL_DURING_TESTING = false;

      private final AjBuildManager buildManager;
      private boolean couldBeSubsequentIncrementalBuild = false;
      private INameEnvironment nameEnvironment;
      //
      // private IHierarchy structureModel;
      // private IRelationshipMap relmap;
      private AsmManager structureModel;

      /**
       * When looking at changes on the classpath, this set accumulates files in our state instance that affected by those changes.
       * Then if we can do an incremental build - these must be compiled.
       */
00085       private final Set affectedFiles = new HashSet();

      private long lastSuccessfulFullBuildTime = -1;
      private final Hashtable /* File, long */structuralChangesSinceLastFullBuild = new Hashtable();

      private long lastSuccessfulBuildTime = -1;
      private long currentBuildTime = -1;

      private AjBuildConfig buildConfig;

      private boolean batchBuildRequiredThisTime = false;

      /**
       * Keeps a list of (FQN,Filename) pairs (as ClassFile objects) for types that resulted from the compilation of the given File.
       * Note :- the ClassFile objects contain no byte code, they are simply a Filename,typename pair.
       * 
       * Populated in noteResult and used in addDependentsOf(File)
       * 
       * Added by AMC during state refactoring, 1Q06.
       */
00105       private final Map/* <File, List<ClassFile> */fullyQualifiedTypeNamesResultingFromCompilationUnit = new HashMap();

      /**
       * Source files defining aspects
       * 
       * Populated in noteResult and used in processDeletedFiles
       * 
       * Added by AMC during state refactoring, 1Q06.
       */
00114       private final Set/* <File> */sourceFilesDefiningAspects = new HashSet();

      /**
       * Populated in noteResult to record the set of types that should be recompiled if the given file is modified or deleted.
       * 
       * Refered to during addAffectedSourceFiles when calculating incremental compilation set.
       */
00121       private final Map/* <File, ReferenceCollection> */references = new HashMap();

      /**
       * Holds UnwovenClassFiles (byte[]s) originating from the given file source. This could be a jar file, a directory, or an
       * individual .class file. This is an *expensive* map. It is cleared immediately following a batch build, and the cheaper
       * inputClassFilesBySource map is kept for processing of any subsequent incremental builds.
       * 
       * Populated during AjBuildManager.initBcelWorld().
       * 
       * Passed into AjCompiler adapter as the set of binary input files to reweave if the weaver determines a full weave is required.
       * 
       * Cleared during initBcelWorld prior to repopulation.
       * 
       * Used when a file is deleted during incremental compilation to delete all of the class files in the output directory that
       * resulted from the weaving of File.
       * 
       * Used during getBinaryFilesToCompile when compiling incrementally to determine which files should be recompiled if a given
       * input file has changed.
       * 
       */
00141       private Map/* File, List<UnwovenClassFile> */binarySourceFiles = new HashMap();

      /**
       * Initially a duplicate of the information held in binarySourceFiles, with the key difference that the values are ClassFiles
       * (type name, File) not UnwovenClassFiles (which also have all the byte code in them). After a batch build, binarySourceFiles
       * is cleared, leaving just this much lighter weight map to use in processing subsequent incremental builds.
       */
00148       private final Map/* <File,List<ClassFile> */inputClassFilesBySource = new HashMap();

      /**
       * A list of the .class files created by this state that contain aspects.
       */
00153       private final List/* <String> */aspectClassFiles = new ArrayList();

      /**
       * Holds structure information on types as they were at the end of the last build. It would be nice to get rid of this too, but
       * can't see an easy way to do that right now.
       */
00159       private final Map/* FQN,CompactStructureRepresentation */resolvedTypeStructuresFromLastBuild = new HashMap();

      /**
       * Populated in noteResult to record the set of UnwovenClassFiles (intermediate results) that originated from compilation of the
       * class with the given fully-qualified name.
       * 
       * Used in removeAllResultsOfLastBuild to remove .class files from output directory.
       * 
       * Passed into StatefulNameEnvironment during incremental compilation to support findType lookups.
       */
00169       private final Map/* <String, File> */classesFromName = new HashMap();

      /**
       * Populated by AjBuildManager to record the aspects with the file name in which they're contained. This is later used when
       * writing the outxml file in AjBuildManager. Need to record the file name because want to write an outxml file for each of the
       * output directories and in order to ask the OutputLocationManager for the output location for a given aspect we need the file
       * in which it is contained.
       */
00177       private Map /* <String, char[]> */aspectsFromFileNames;

      private Set/* File */compiledSourceFiles = new HashSet();
      private final Map/* String,File sourcelocation */resources = new HashMap();

      // these are references created on a particular compile run - when looping round in
      // addAffectedSourceFiles(), if some have been created then we look at which source files
      // touch upon those and get them recompiled.
      private StringSet qualifiedStrings = new StringSet(3);

      private StringSet simpleStrings = new StringSet(3);

      private Set addedFiles;
      private Set deletedFiles;
      private Set /* BinarySourceFile */addedBinaryFiles;
      private Set /* BinarySourceFile */deletedBinaryFiles;

      private BcelWeaver weaver;
      private BcelWorld world;

      public AjState(AjBuildManager buildManager) {
            this.buildManager = buildManager;
      }

      public void setCouldBeSubsequentIncrementalBuild(boolean yesThereCould) {
            this.couldBeSubsequentIncrementalBuild = yesThereCould;
      }

      void successfulCompile(AjBuildConfig config, boolean wasFullBuild) {
            buildConfig = config;
            lastSuccessfulBuildTime = currentBuildTime;
            if (stateListener != null)
                  stateListener.buildSuccessful(wasFullBuild);
            if (wasFullBuild)
                  lastSuccessfulFullBuildTime = currentBuildTime;
      }

      /**
       * Returns false if a batch build is needed.
       */
00217       public boolean prepareForNextBuild(AjBuildConfig newBuildConfig) {
            currentBuildTime = System.currentTimeMillis();

            if (!maybeIncremental()) {
                  if (listenerDefined())
                        getListener().recordDecision(
                                    "Preparing for build: not going to be incremental because either not in AJDT or incremental deactivated");
                  return false;
            }

            if (this.batchBuildRequiredThisTime) {
                  this.batchBuildRequiredThisTime = false;
                  if (listenerDefined())
                        getListener().recordDecision(
                                    "Preparing for build: not going to be incremental this time because batch build explicitly forced");
                  return false;
            }

            if (lastSuccessfulBuildTime == -1 || buildConfig == null) {
                  structuralChangesSinceLastFullBuild.clear();
                  if (listenerDefined())
                        getListener().recordDecision(
                                    "Preparing for build: not going to be incremental because no successful previous full build");
                  return false;
            }

            // we don't support incremental with an outjar yet
            if (newBuildConfig.getOutputJar() != null) {
                  structuralChangesSinceLastFullBuild.clear();
                  if (listenerDefined())
                        getListener().recordDecision("Preparing for build: not going to be incremental because outjar being used");
                  return false;
            }

            affectedFiles.clear();

            // we can't do an incremental build if one of our paths
            // has changed, or a jar on a path has been modified
            if (pathChange(buildConfig, newBuildConfig)) {
                  // last time we built, .class files and resource files from jars on the
                  // inpath will have been copied to the output directory.
                  // these all need to be deleted in preparation for the clean build that is
                  // coming - otherwise a file that has been deleted from an inpath jar
                  // since the last build will not be deleted from the output directory.
                  removeAllResultsOfLastBuild();
                  if (stateListener != null) {
                        stateListener.pathChangeDetected();
                  }
                  structuralChangesSinceLastFullBuild.clear();
                  if (listenerDefined())
                        getListener()
                                    .recordDecision(
                                                "Preparing for build: not going to be incremental because path change detected (one of classpath/aspectpath/inpath/injars)");
                  return false;
            }

            if (simpleStrings.elementSize > 20) {
                  simpleStrings = new StringSet(3);
            } else {
                  simpleStrings.clear();
            }
            if (qualifiedStrings.elementSize > 20) {
                  qualifiedStrings = new StringSet(3);
            } else {
                  qualifiedStrings.clear();
            }

            if ((newBuildConfig.getChanged() & PROJECTSOURCEFILES_CHANGED) == 0) {
                  addedFiles = Collections.EMPTY_SET;
                  deletedFiles = Collections.EMPTY_SET;
            } else {
                  Set oldFiles = new HashSet(buildConfig.getFiles());
                  Set newFiles = new HashSet(newBuildConfig.getFiles());

                  addedFiles = new HashSet(newFiles);
                  addedFiles.removeAll(oldFiles);
                  deletedFiles = new HashSet(oldFiles);
                  deletedFiles.removeAll(newFiles);
            }

            Set oldBinaryFiles = new HashSet(buildConfig.getBinaryFiles());
            Set newBinaryFiles = new HashSet(newBuildConfig.getBinaryFiles());

            addedBinaryFiles = new HashSet(newBinaryFiles);
            addedBinaryFiles.removeAll(oldBinaryFiles);
            deletedBinaryFiles = new HashSet(oldBinaryFiles);
            deletedBinaryFiles.removeAll(newBinaryFiles);

            boolean couldStillBeIncremental = processDeletedFiles(deletedFiles);

            if (!couldStillBeIncremental) {
                  if (listenerDefined())
                        getListener().recordDecision("Preparing for build: not going to be incremental because an aspect was deleted");
                  return false;
            }

            if (listenerDefined()) {
                  getListener().recordDecision("Preparing for build: planning to be an incremental build");
            }
            return true;
      }

      /**
       * Checks if any of the files in the set passed in contains an aspect declaration. If one is found then we start the process of
       * batch building, i.e. we remove all the results of the last build, call any registered listener to tell them whats happened
       * and return false.
       * 
       * @return false if we discovered an aspect declaration
       */
00326       private boolean processDeletedFiles(Set deletedFiles) {
            for (Iterator iter = deletedFiles.iterator(); iter.hasNext();) {
                  File aDeletedFile = (File) iter.next();
                  if (this.sourceFilesDefiningAspects.contains(aDeletedFile)) {
                        removeAllResultsOfLastBuild();
                        if (stateListener != null) {
                              stateListener.detectedAspectDeleted(aDeletedFile);
                        }
                        return false;
                  }
                  List/* ClassFile */classes = (List) fullyQualifiedTypeNamesResultingFromCompilationUnit.get(aDeletedFile);
                  if (classes != null) {
                        for (Iterator iterator = classes.iterator(); iterator.hasNext();) {
                              ClassFile element = (ClassFile) iterator.next();
                              resolvedTypeStructuresFromLastBuild.remove(element.fullyQualifiedTypeName);
                        }
                  }
            }
            return true;
      }

      private Collection getModifiedFiles() {
            return getModifiedFiles(lastSuccessfulBuildTime);
      }

      Collection getModifiedFiles(long lastBuildTime) {
            Set ret = new HashSet();

            // Check if the build configuration knows what files have changed...
            List/* File */modifiedFiles = buildConfig.getModifiedFiles();

            if (modifiedFiles == null) {
                  // do not know, so need to go looking
                  // not our job to account for new and deleted files
                  for (Iterator i = buildConfig.getFiles().iterator(); i.hasNext();) {
                        File file = (File) i.next();
                        if (!file.exists())
                              continue;

                        long modTime = file.lastModified();
                        // System.out.println("check: " + file + " mod " + modTime + " build " + lastBuildTime);
                        // need to add 1000 since lastModTime is only accurate to a second on some (all?) platforms
                        if (modTime + 1000 > lastBuildTime) {
                              ret.add(file);
                        }
                  }
            } else {
                  ret.addAll(modifiedFiles);
            }
            ret.addAll(affectedFiles);
            return ret;
      }

      private Collection getModifiedBinaryFiles() {
            return getModifiedBinaryFiles(lastSuccessfulBuildTime);
      }

      Collection getModifiedBinaryFiles(long lastBuildTime) {
            List ret = new ArrayList();
            // not our job to account for new and deleted files
            for (Iterator i = buildConfig.getBinaryFiles().iterator(); i.hasNext();) {
                  AjBuildConfig.BinarySourceFile bsfile = (AjBuildConfig.BinarySourceFile) i.next();
                  File file = bsfile.binSrc;
                  if (!file.exists())
                        continue;

                  long modTime = file.lastModified();
                  // System.out.println("check: " + file + " mod " + modTime + " build " + lastBuildTime);
                  // need to add 1000 since lastModTime is only accurate to a second on some (all?) platforms
                  if (modTime + 1000 >= lastBuildTime) {
                        ret.add(bsfile);
                  }
            }
            return ret;
      }

      private static int CLASS_FILE_NO_CHANGES = 0;

      private static int CLASS_FILE_CHANGED_THAT_NEEDS_INCREMENTAL_BUILD = 1;

      private static int CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD = 2;

      public static final FileFilter classFileFilter = new FileFilter() {
            public boolean accept(File pathname) {
                  return pathname.getName().endsWith(".class");
            }
      };
      
      private void recordDecision(String decision) {
            getListener().recordDecision(decision);
      }

      /**
       * Analyse .class files in the directory specified, if they have changed since the last successful build then see if we can
       * determine which source files in our project depend on the change. If we can then we can still do an incremental build, if we
       * can't then we have to do a full build.
       * 
       */
00424       private int classFileChangedInDirSinceLastBuildRequiringFullBuild(File dir, int pathid) {

            if (!dir.isDirectory()) {
                  if (listenerDefined()) {
                        recordDecision("ClassFileChangeChecking: not a directory so forcing full build: '"+dir.getPath()+"'");
                  }
                  return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
            }

            // Are we managing that output directory?
            AjState state = IncrementalStateManager.findStateManagingOutputLocation(dir);
            if (listenerDefined()) {
                  if (state != null) {
                        recordDecision("ClassFileChangeChecking: found state instance managing output location : " + dir);
                  } else {
                        recordDecision("ClassFileChangeChecking: failed to find a state instance managing output location : " + dir);
                  }
            }
            

            // pr268827 - this guard will cause us to exit quickly if the state says there really is
            // nothing of interest. This will not catch the case where a user modifies the .class files outside of
            // eclipse because the state will not be aware of it. But that seems an unlikely scenario and
            // we are paying a heavy price to check it
            if (state != null && !state.hasAnyStructuralChangesSince(lastSuccessfulBuildTime)) {
                  if (listenerDefined()) {
                        getListener().recordDecision("ClassFileChangeChecking: no reported changes in that state");
                  }
                  return CLASS_FILE_NO_CHANGES;
            }
            
            if (state == null) {
                  // This may be because the directory is the output path of a Java project upon which we depend
                  // we need to call back into AJDT to ask about that projects state.
                  CompilationResultDestinationManager crdm = buildConfig.getCompilationResultDestinationManager();
                  if (crdm!=null) {
                        int i = crdm.discoverChangesSince(dir,lastSuccessfulBuildTime);
                        // 0 = dontknow if it has changed
                        // 1 = definetly not changed at all
                        // further numbers can determine more granular changes
                        if (i==1) {
                              if (listenerDefined()) {
                                    getListener().recordDecision("ClassFileChangeChecking: queried JDT and '"+dir+"' is apparently unchanged so not performing timestamp check");
                              }
                              return CLASS_FILE_NO_CHANGES;
                        }
                  }
            }

            List classFiles = FileUtil.listClassFiles(dir);

            for (Iterator iterator = classFiles.iterator(); iterator.hasNext();) {
                  File classFile = (File) iterator.next();
                  if (CHECK_STATE_FIRST && state != null) {
                        // Next section reworked based on bug 270033: 
                        // if it is an aspect we may or may not be in trouble depending on whether (a) we depend on it (b) it is on the
                        // classpath or the aspectpath
                        if (state.isAspect(classFile)) {
                              if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime) ||
                                    isTypeWeReferTo(classFile)) {     
                                    // further improvements possible    
                                    if (listenerDefined()) {
                                          getListener().recordDecision("ClassFileChangeChecking: aspect found that has structurally changed or that this project depends upon : " + classFile);
                                    }
                                    return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
                              } else {
                                  // it is an aspect but we don't refer to it: 
                                  // - for CLASSPATH I think this is OK, we can continue and try an
                                  //   incremental build
                                  // - for ASPECTPATH we don't know what else might be touched in this project
                                  //   and must rebuild
                                    if (pathid==PATHID_CLASSPATH) {
                                          if (listenerDefined()) {
                                                getListener().recordDecision("ClassFileChangeChecking: found aspect on classpath but this project doesn't reference it, continuing to try for incremental build : " + classFile);
                                          }                                         
                                    } else {
                                          if (listenerDefined()) {
                                                getListener().recordDecision("ClassFileChangeChecking: found aspect on aspectpath/inpath - can't determine if this project is affected, must full build: " + classFile);
                                          }
                                          return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
                                    }
                                }

                        }
                        if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime)) {
                              if (listenerDefined()) {
                                    getListener().recordDecision("ClassFileChangeChecking: structural change detected in : " + classFile);
                              }
                              isTypeWeReferTo(classFile);
                        }
                  } else {
                        long modTime = classFile.lastModified();
                        if ((modTime + 1000) >= lastSuccessfulBuildTime) {
                              // so the class on disk has changed since the last successful build for this state object

                              // BUG? we stop on the first change that leads us to an incremental build, surely we need to continue and look
                              // at all files incase another change means we need to incremental a bit more stuff?

                              // To work out if it is a real change we should ask any state
                              // object managing the output location whether the file has
                              // structurally changed or not
                              if (state != null) {
                                    if (state.isAspect(classFile)) {
                                          if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime) ||
                                                      isTypeWeReferTo(classFile)) {     
                                                // further improvements possible                      
                                                if (listenerDefined()) {
                                                      getListener().recordDecision("ClassFileChangeChecking: aspect found that has structurally changed or that this project depends upon : " + classFile);
                                                }
                                                return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
                                          } else {
                                              // it is an aspect but we don't refer to it: 
                                              // - for CLASSPATH I think this is OK, we can continue and try an
                                              //   incremental build
                                              // - for ASPECTPATH we don't know what else might be touched in this project
                                              //   and must rebuild
                                                if (pathid==PATHID_CLASSPATH) {
                                                      if (listenerDefined()) {
                                                            getListener().recordDecision("ClassFileChangeChecking: found aspect on classpath but this project doesn't reference it, continuing to try for incremental build : " + classFile);
                                                      }
                                                } else {
                                                      if (listenerDefined()) {
                                                            getListener().recordDecision("ClassFileChangeChecking: found aspect on aspectpath/inpath - can't determine if this project is affected, must full build: " + classFile);
                                                      }
                                                      return CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD;
                                                }
                                            }
                                    }
                                    if (state.hasStructuralChangedSince(classFile, lastSuccessfulBuildTime)) {
                                          if (listenerDefined()) {
                                                getListener().recordDecision("ClassFileChangeChecking: structural change detected in : " + classFile);
                                          }
                                          isTypeWeReferTo(classFile);
                                    } else {
                                          if (listenerDefined())
                                                getListener().recordDecision("ClassFileChangeChecking: change detected in " + classFile + " but it is not structural");
                                    }
                              } else {
                                    // No state object to ask, so it only matters if we know which type depends on this file
                                    if (isTypeWeReferTo(classFile)) {
                                          return CLASS_FILE_CHANGED_THAT_NEEDS_INCREMENTAL_BUILD;
                                    } else {
                                          return CLASS_FILE_NO_CHANGES;
                                    }
                              }
                        }
                  }
            }
            return CLASS_FILE_NO_CHANGES;
      }

      private boolean isAspect(File file) {
            return aspectClassFiles.contains(file.getAbsolutePath());
      }

      public static class SoftHashMap extends AbstractMap {

            private final Map map;

            private final ReferenceQueue rq = new ReferenceQueue();

            public SoftHashMap(Map map) {
                  this.map = map;
            }

            public SoftHashMap() {
                  this(new HashMap());
            }

            public SoftHashMap(Map map, boolean b) {
                  this(map);
            }

            class SoftReferenceKnownKey extends SoftReference {

                  private final Object key;

                  SoftReferenceKnownKey(Object k, Object v) {
                        super(v, rq);
                        this.key = k;
                  }
            }

            private void processQueue() {
                  SoftReferenceKnownKey sv = null;
                  while ((sv = (SoftReferenceKnownKey) rq.poll()) != null) {
                        map.remove(sv.key);
                  }
            }

            public Object get(Object key) {
                  SoftReferenceKnownKey value = (SoftReferenceKnownKey) map.get(key);
                  if (value == null)
                        return null;
                  if (value.get() == null) {
                        // it got GC'd
                        map.remove(value.key);
                        return null;
                  } else {
                        return value.get();
                  }
            }

            public Object put(Object k, Object v) {
                  processQueue();
                  return map.put(k, new SoftReferenceKnownKey(k, v));
            }

            public Set entrySet() {
                  return map.entrySet();
            }

            public void clear() {
                  processQueue();
                  map.clear();
            }

            public int size() {
                  processQueue();
                  return map.size();
            }

            public Object remove(Object k) {
                  processQueue();
                  SoftReferenceKnownKey value = (SoftReferenceKnownKey) map.remove(k);
                  if (value == null)
                        return null;
                  if (value.get() != null) {
                        return value.get();
                  }
                  return null;
            }
      }

      SoftHashMap/* <baseDir,SoftHashMap<theFile,className>> */fileToClassNameMap = new SoftHashMap();

      /**
       * If a class file has changed in a path on our classpath, it may not be for a type that any of our source files care about.
       * This method checks if any of our source files have a dependency on the class in question and if not, we don't consider it an
       * interesting change.
       */
00665       private boolean isTypeWeReferTo(File file) {
            String fpath = file.getAbsolutePath();
            int finalSeparator = fpath.lastIndexOf(File.separator);
            String baseDir = fpath.substring(0, finalSeparator);
            String theFile = fpath.substring(finalSeparator + 1);
            SoftHashMap classNames = (SoftHashMap) fileToClassNameMap.get(baseDir);
            if (classNames == null) {
                  classNames = new SoftHashMap();
                  fileToClassNameMap.put(baseDir, classNames);
            }
            char[] className = (char[]) classNames.get(theFile);
            if (className == null) {
                  // if (listenerDefined())
                  // getListener().recordDecision("Cache miss, looking up classname for : " + fpath);

                  ClassFileReader cfr;
                  try {
                        cfr = ClassFileReader.read(file);
                  } catch (ClassFormatException e) {
                        return true;
                  } catch (IOException e) {
                        return true;
                  }
                  className = cfr.getName();
                  classNames.put(theFile, className);
                  // } else {
                  // if (listenerDefined())
                  // getListener().recordDecision("Cache hit, looking up classname for : " + fpath);
            }

            char[][][] qualifiedNames = null;
            char[][] simpleNames = null;
            if (CharOperation.indexOf('/', className) != -1) {
                  qualifiedNames = new char[1][][];
                  qualifiedNames[0] = CharOperation.splitOn('/', className);
                  qualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedNames);
            } else {
                  simpleNames = new char[1][];
                  simpleNames[0] = className;
                  simpleNames = ReferenceCollection.internSimpleNames(simpleNames, true);
            }
            int newlyAffectedFiles = 0;
            for (Iterator i = references.entrySet().iterator(); i.hasNext();) {
                  Map.Entry entry = (Map.Entry) i.next();
                  ReferenceCollection refs = (ReferenceCollection) entry.getValue();
                  if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
                        if (listenerDefined()) {
                              getListener().recordDecision(
                                          toString() + ": type " + new String(className) + " is depended upon by '" + entry.getKey() + "'");
                        }
                        newlyAffectedFiles++;
                        // possibly the beginnings of addressing the second point in 270033 comment 3
//                      List/*ClassFile*/ cfs = (List)this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(entry.getKey());
                        affectedFiles.add(entry.getKey());
                  }
            }
            if (newlyAffectedFiles>0) {
                  return true;
            }
            if (listenerDefined()) {
                  getListener().recordDecision(toString() + ": type " + new String(className) + " is not depended upon by this state");
            }
            return false;
      }

      // /**
      // * For a given class file, determine which source file it came from. This will only succeed if the class file is from a source
      // * file within this project.
      // */
      // private File getSourceFileForClassFile(File classfile) {
      // Set sourceFiles = fullyQualifiedTypeNamesResultingFromCompilationUnit.keySet();
      // for (Iterator sourceFileIterator = sourceFiles.iterator(); sourceFileIterator.hasNext();) {
      // File sourceFile = (File) sourceFileIterator.next();
      // List/* ClassFile */classesFromSourceFile = (List/* ClassFile */) fullyQualifiedTypeNamesResultingFromCompilationUnit
      // .get(sourceFile);
      // for (int i = 0; i < classesFromSourceFile.size(); i++) {
      // if (((ClassFile) classesFromSourceFile.get(i)).locationOnDisk.equals(classfile))
      // return sourceFile;
      // }
      // }
      // return null;
      // }

00748       public String toString() {
            StringBuffer sb = new StringBuffer();
            // null config means failed build i think as it is only set on successful full build?
            sb.append("AjState(").append((buildConfig == null ? "NULLCONFIG" : buildConfig.getConfigFile().toString())).append(")");
            return sb.toString();
      }

      /**
       * Determine if a file has changed since a given time, using the local information recorded in the structural changes data
       * structure.
       * 
       * @param file the file we are wondering about
       * @param lastSuccessfulBuildTime the last build time for the state asking the question
       */
00762       private boolean hasStructuralChangedSince(File file, long lastSuccessfulBuildTime) {
            // long lastModTime = file.lastModified();
            Long l = (Long) structuralChangesSinceLastFullBuild.get(file.getAbsolutePath());
            long strucModTime = -1;
            if (l != null)
                  strucModTime = l.longValue();
            else
                  strucModTime = this.lastSuccessfulFullBuildTime;
            // we now have:
            // 'strucModTime'-> the last time the class was structurally changed
            return (strucModTime > lastSuccessfulBuildTime);
      }

      /**
       * Determine if anything has changed since a given time.
       */
00778       private boolean hasAnyStructuralChangesSince(long lastSuccessfulBuildTime) {
            Set entries = structuralChangesSinceLastFullBuild.entrySet();
            for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
                  Map.Entry entry = (Map.Entry) iterator.next();
                  Long l = (Long) entry.getValue();
                  if (l != null) {
                        long lvalue = l.longValue();
                        if (lvalue > lastSuccessfulBuildTime) {
                              if (listenerDefined()) {
                                    getListener().recordDecision(
                                                "Seems this has changed " + entry.getKey() + "modtime=" + lvalue + " lsbt="
                                                            + this.lastSuccessfulFullBuildTime + "   incoming check value=" + lastSuccessfulBuildTime);
                              }
                              return true;
                        }
                  }
            }
            return (this.lastSuccessfulFullBuildTime > lastSuccessfulBuildTime);
      }
      
      static int PATHID_CLASSPATH = 0;
      static int PATHID_ASPECTPATH = 1;
      static int PATHID_INPATH = 2;

      /**
       * Determine if something has changed on the classpath/inpath/aspectpath and a full build is required rather than an incremental
       * one.
       * 
       * @param previousConfig the previous configuration used
       * @param newConfig the new configuration being used
       * @return true if full build required
       */
00810       private boolean pathChange(AjBuildConfig previousConfig, AjBuildConfig newConfig) {
            int changes = newConfig.getChanged();

            if ((changes & (CLASSPATH_CHANGED | ASPECTPATH_CHANGED | INPATH_CHANGED | OUTPUTDESTINATIONS_CHANGED | INJARS_CHANGED)) != 0) {
                  List oldOutputLocs = getOutputLocations(previousConfig);

                  Set alreadyAnalysedPaths = new HashSet();

                  List oldClasspath = previousConfig.getClasspath();
                  List newClasspath = newConfig.getClasspath();
                  if (stateListener != null)
                        stateListener.aboutToCompareClasspaths(oldClasspath, newClasspath);
                  if (classpathChangedAndNeedsFullBuild(oldClasspath, newClasspath, true, oldOutputLocs, alreadyAnalysedPaths))
                        return true;

                  List oldAspectpath = previousConfig.getAspectpath();
                  List newAspectpath = newConfig.getAspectpath();
                  if (changedAndNeedsFullBuild(oldAspectpath, newAspectpath, true, oldOutputLocs, alreadyAnalysedPaths, PATHID_ASPECTPATH))
                        return true;

                  List oldInPath = previousConfig.getInpath();
                  List newInPath = newConfig.getInpath();
                  if (changedAndNeedsFullBuild(oldInPath, newInPath, false, oldOutputLocs, alreadyAnalysedPaths, PATHID_INPATH))
                        return true;

                  List oldInJars = previousConfig.getInJars();
                  List newInJars = newConfig.getInJars();
                  if (changedAndNeedsFullBuild(oldInJars, newInJars, false, oldOutputLocs, alreadyAnalysedPaths, PATHID_INPATH))
                        return true;
            } else if (newConfig.getClasspathElementsWithModifiedContents() != null) {
                  // Although the classpath entries themselves are the same as before, the contents of one of the
                  // directories on the classpath has changed - rather than go digging around to find it, let's ask
                  // the compiler configuration. This will allow for projects with long classpaths where classpaths
                  // are also capturing project dependencies - when a project we depend on is rebuilt, we can just check
                  // it as a standalone element on our classpath rather than going through them all
                  List/* String */modifiedCpElements = newConfig.getClasspathElementsWithModifiedContents();
                  for (Iterator iterator = modifiedCpElements.iterator(); iterator.hasNext();) {
                        File cpElement = new File((String) iterator.next());
                        if (cpElement.exists() && !cpElement.isDirectory()) {
                              if (cpElement.lastModified() > lastSuccessfulBuildTime) {
                                    return true;
                              }
                        } else {
                              int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(cpElement, PATHID_CLASSPATH);
                              if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
                                    return true;
                              }
                        }
                  }
            }

            return false;
      }

      /**
       * Return a list of the output locations - this includes any 'default' output location and then any known by a registered
       * CompilationResultDestinationManager.
       * 
       * @param config the build configuration for which the output locations should be determined
       * @return a list of file objects
       */
00871       private List /* File */getOutputLocations(AjBuildConfig config) {
            List outputLocs = new ArrayList();
            // Is there a default location?
            if (config.getOutputDir() != null) {
                  try {
                        outputLocs.add(config.getOutputDir().getCanonicalFile());
                  } catch (IOException e) {
                  }
            }
            if (config.getCompilationResultDestinationManager() != null) {
                  List dirs = config.getCompilationResultDestinationManager().getAllOutputLocations();
                  for (Iterator iterator = dirs.iterator(); iterator.hasNext();) {
                        File f = (File) iterator.next();
                        try {
                              File cf = f.getCanonicalFile();
                              if (!outputLocs.contains(cf)) {
                                    outputLocs.add(cf);
                              }
                        } catch (IOException e) {
                        }
                  }
            }
            return outputLocs;
      }

      private File getOutputLocationFor(AjBuildConfig config, File aResourceFile) {
            List outputLocs = new ArrayList();
            if (config.getCompilationResultDestinationManager() != null) {
                  File outputLoc = config.getCompilationResultDestinationManager().getOutputLocationForResource(aResourceFile);
                  if (outputLoc != null)
                        return outputLoc;
            }
            // Is there a default location?
            if (config.getOutputDir() != null) {
                  return config.getOutputDir();
            }
            return null;
      }

      /**
       * Check the old and new paths, if they vary by length or individual elements then that is considered a change. Or if the last
       * modified time of a path entry has changed (or last modified time of a classfile in that path entry has changed) then return
       * true. The outputlocations are supplied so they can be 'ignored' in the comparison.
       * 
       * @param oldPath
       * @param newPath
       * @param checkClassFiles whether to examine individual class files within directories
       * @param outputLocs the output locations that should be ignored if they occur on the paths being compared
       * @return true if a change is detected that requires a full build
       */
00921       private boolean changedAndNeedsFullBuild(List oldPath, List newPath, boolean checkClassFiles, List outputLocs,
                  Set alreadyAnalysedPaths, int pathid) {
            if (oldPath.size() != newPath.size()) {
                  return true;
            }
            for (int i = 0; i < oldPath.size(); i++) {
                  if (!oldPath.get(i).equals(newPath.get(i))) {
                        return true;
                  }
                  Object o = oldPath.get(i); // String on classpath, File on other paths
                  File f = null;
                  if (o instanceof String) {
                        f = new File((String) o);
                  } else {
                        f = (File) o;
                  }
                  if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
                        return true;
                  }
                  if (checkClassFiles && f.exists() && f.isDirectory()) {

                        // We should use here a list/set of directories we know have or have not changed - some kind of
                        // List<File> buildConfig.getClasspathEntriesWithChangedContents()
                        // and then only proceed to look inside directories if it is one of these, ignoring others -
                        // that should save a massive amount of processing for incremental builds in a multi project scenario

                        boolean foundMatch = false;
                        for (Iterator iterator = outputLocs.iterator(); !foundMatch && iterator.hasNext();) {
                              File dir = (File) iterator.next();
                              if (f.equals(dir)) {
                                    foundMatch = true;
                              }
                        }
                        if (!foundMatch) {
                              if (!alreadyAnalysedPaths.contains(f.getAbsolutePath())) { // Do not check paths more than once
                                    alreadyAnalysedPaths.add(f.getAbsolutePath());
                                    int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(f,pathid);
                                    if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD) {
                                          return true;
                                    }
                              }
                        }
                  }
            }
            return false;
      }

      /**
       * Check the old and new paths, if they vary by length or individual elements then that is considered a change. Or if the last
       * modified time of a path entry has changed (or last modified time of a classfile in that path entry has changed) then return
       * true. The outputlocations are supplied so they can be 'ignored' in the comparison.
       * 
       * @param oldPath
       * @param newPath
       * @param checkClassFiles whether to examine individual class files within directories
       * @param outputLocs the output locations that should be ignored if they occur on the paths being compared
       * @return true if a change is detected that requires a full build
       */
00979       private boolean classpathChangedAndNeedsFullBuild(List oldPath, List newPath, boolean checkClassFiles, List outputLocs,
                  Set alreadyAnalysedPaths) {
            if (oldPath.size() != newPath.size()) {
                  return true;
            }
            for (int i = 0; i < oldPath.size(); i++) {
                  if (!oldPath.get(i).equals(newPath.get(i))) {
                        return true;
                  }
                  File f = new File((String) oldPath.get(i));
                  if (f.exists() && !f.isDirectory() && (f.lastModified() >= lastSuccessfulBuildTime)) {
                        return true;
                  }
                  if (checkClassFiles && f.exists() && f.isDirectory()) {

                        // We should use here a list/set of directories we know have or have not changed - some kind of
                        // List<File> buildConfig.getClasspathEntriesWithChangedContents()
                        // and then only proceed to look inside directories if it is one of these, ignoring others -
                        // that should save a massive amount of processing for incremental builds in a multi project scenario

                        boolean foundMatch = false;
                        for (Iterator iterator = outputLocs.iterator(); !foundMatch && iterator.hasNext();) {
                              File dir = (File) iterator.next();
                              if (f.equals(dir)) {
                                    foundMatch = true;
                              }
                        }
                        if (!foundMatch) {
                              if (!alreadyAnalysedPaths.contains(f.getAbsolutePath())) { // Do not check paths more than once
                                    alreadyAnalysedPaths.add(f.getAbsolutePath());
                                    int classFileChanges = classFileChangedInDirSinceLastBuildRequiringFullBuild(f,PATHID_CLASSPATH);
                                    if (classFileChanges == CLASS_FILE_CHANGED_THAT_NEEDS_FULL_BUILD)
                                          return true;
                              }
                        }
                  }
            }
            return false;
      }

      public Set getFilesToCompile(boolean firstPass) {
            Set thisTime = new HashSet();
            if (firstPass) {
                  compiledSourceFiles = new HashSet();
                  Collection modifiedFiles = getModifiedFiles();
                  // System.out.println("modified: " + modifiedFiles);
                  thisTime.addAll(modifiedFiles);
                  // ??? eclipse IncrementalImageBuilder appears to do this
                  // for (Iterator i = modifiedFiles.iterator(); i.hasNext();) {
                  // File file = (File) i.next();
                  // addDependentsOf(file);
                  // }

                  if (addedFiles != null) {
                        for (Iterator fIter = addedFiles.iterator(); fIter.hasNext();) {
                              Object o = fIter.next();
                              if (!thisTime.contains(o))
                                    thisTime.add(o);
                        }
                        // thisTime.addAll(addedFiles);
                  }

                  deleteClassFiles();
                  // Do not delete resources on incremental build, AJDT will handle
                  // copying updates to the output folder. AspectJ only does a copy
                  // of them on full build (see copyResourcesToDestination() call
                  // in AjBuildManager)
                  // deleteResources();

                  addAffectedSourceFiles(thisTime, thisTime);
            } else {
                  addAffectedSourceFiles(thisTime, compiledSourceFiles);
            }
            compiledSourceFiles = thisTime;
            return thisTime;
      }

      private boolean maybeIncremental() {
            return (FORCE_INCREMENTAL_DURING_TESTING || this.couldBeSubsequentIncrementalBuild);
      }

      public Map /* String -> List<ucf> */getBinaryFilesToCompile(boolean firstTime) {
            if (lastSuccessfulBuildTime == -1 || buildConfig == null || !maybeIncremental()) {
                  return binarySourceFiles;
            }
            // else incremental...
            Map toWeave = new HashMap();
            if (firstTime) {
                  List addedOrModified = new ArrayList();
                  addedOrModified.addAll(addedBinaryFiles);
                  addedOrModified.addAll(getModifiedBinaryFiles());
                  for (Iterator iter = addedOrModified.iterator(); iter.hasNext();) {
                        AjBuildConfig.BinarySourceFile bsf = (AjBuildConfig.BinarySourceFile) iter.next();
                        UnwovenClassFile ucf = createUnwovenClassFile(bsf);
                        if (ucf == null)
                              continue;
                        List ucfs = new ArrayList();
                        ucfs.add(ucf);
                        recordTypeChanged(ucf.getClassName());
                        binarySourceFiles.put(bsf.binSrc.getPath(), ucfs);
                        List cfs = new ArrayList(1);
                        cfs.add(getClassFileFor(ucf));
                        this.inputClassFilesBySource.put(bsf.binSrc.getPath(), cfs);
                        toWeave.put(bsf.binSrc.getPath(), ucfs);
                  }
                  deleteBinaryClassFiles();
            } else {
                  // return empty set... we've already done our bit.
            }
            return toWeave;
      }

      /**
       * Called when a path change is about to trigger a full build, but we haven't cleaned up from the last incremental build...
       */
01094       private void removeAllResultsOfLastBuild() {
            // remove all binarySourceFiles, and all classesFromName...
            for (Iterator iter = this.inputClassFilesBySource.values().iterator(); iter.hasNext();) {
                  List cfs = (List) iter.next();
                  for (Iterator iterator = cfs.iterator(); iterator.hasNext();) {
                        ClassFile cf = (ClassFile) iterator.next();
                        cf.deleteFromFileSystem(buildConfig);
                  }
            }
            for (Iterator iterator = classesFromName.values().iterator(); iterator.hasNext();) {
                  File f = (File) iterator.next();
                  new ClassFile("", f).deleteFromFileSystem(buildConfig);
            }
            Set resourceEntries = resources.entrySet();
            for (Iterator iter = resourceEntries.iterator(); iter.hasNext();) {
                  Map.Entry resourcePair = (Map.Entry) iter.next();
                  File sourcePath = (File) resourcePair.getValue();
                  File outputLoc = getOutputLocationFor(buildConfig, sourcePath);
                  if (outputLoc != null) {
                        outputLoc = new File(outputLoc, (String) resourcePair.getKey());
                        if (!outputLoc.getPath().equals(sourcePath.getPath()) && outputLoc.exists()) {
                              outputLoc.delete();
                              if (buildConfig.getCompilationResultDestinationManager() != null) {
                                    buildConfig.getCompilationResultDestinationManager().reportFileRemove(outputLoc.getPath(),
                                                CompilationResultDestinationManager.FILETYPE_RESOURCE);
                              }
                        }
                  }
            }
      }

      private void deleteClassFiles() {
            if (deletedFiles == null) {
                  return;
            }
            for (Iterator i = deletedFiles.iterator(); i.hasNext();) {
                  File deletedFile = (File) i.next();
                  addDependentsOf(deletedFile);

                  List cfs = (List) this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(deletedFile);
                  this.fullyQualifiedTypeNamesResultingFromCompilationUnit.remove(deletedFile);

                  if (cfs != null) {
                        for (Iterator iter = cfs.iterator(); iter.hasNext();) {
                              ClassFile cf = (ClassFile) iter.next();
                              deleteClassFile(cf);
                        }
                  }

            }
      }

      private void deleteBinaryClassFiles() {
            // range of bsf is ucfs, domain is files (.class and jars) in inpath/jars
            for (Iterator iter = deletedBinaryFiles.iterator(); iter.hasNext();) {
                  AjBuildConfig.BinarySourceFile deletedFile = (AjBuildConfig.BinarySourceFile) iter.next();
                  List cfs = (List) this.inputClassFilesBySource.get(deletedFile.binSrc.getPath());
                  for (Iterator iterator = cfs.iterator(); iterator.hasNext();) {
                        deleteClassFile((ClassFile) iterator.next());
                  }
                  this.inputClassFilesBySource.remove(deletedFile.binSrc.getPath());
            }
      }

      // private void deleteResources() {
      // List oldResources = new ArrayList();
      // oldResources.addAll(resources);
      //
      // // note - this deliberately ignores resources in jars as we don't yet handle jar changes
      // // with incremental compilation
      // for (Iterator i = buildConfig.getInpath().iterator(); i.hasNext();) {
      // File inPathElement = (File) i.next();
      // if (inPathElement.isDirectory() && AjBuildManager.COPY_INPATH_DIR_RESOURCES) {
      // deleteResourcesFromDirectory(inPathElement, oldResources);
      // }
      // }
      //
      // if (buildConfig.getSourcePathResources() != null) {
      // for (Iterator i = buildConfig.getSourcePathResources().keySet().iterator(); i.hasNext();) {
      // String resource = (String) i.next();
      // maybeDeleteResource(resource, oldResources);
      // }
      // }
      //
      // // oldResources need to be deleted...
      // for (Iterator iter = oldResources.iterator(); iter.hasNext();) {
      // String victim = (String) iter.next();
      // List outputDirs = getOutputLocations(buildConfig);
      // for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
      // File dir = (File) iterator.next();
      // File f = new File(dir, victim);
      // if (f.exists()) {
      // f.delete();
      // }
      // resources.remove(victim);
      // }
      // }
      // }

      // private void maybeDeleteResource(String resName, List oldResources) {
      // if (resources.contains(resName)) {
      // oldResources.remove(resName);
      // List outputDirs = getOutputLocations(buildConfig);
      // for (Iterator iterator = outputDirs.iterator(); iterator.hasNext();) {
      // File dir = (File) iterator.next();
      // File source = new File(dir, resName);
      // if (source.exists() && (source.lastModified() >= lastSuccessfulBuildTime)) {
      // resources.remove(resName); // will ensure it is re-copied
      // }
      // }
      // }
      // }

      // private void deleteResourcesFromDirectory(File dir, List oldResources) {
      // File[] files = FileUtil.listFiles(dir, new FileFilter() {
      // public boolean accept(File f) {
      // boolean accept = !(f.isDirectory() || f.getName().endsWith(".class"));
      // return accept;
      // }
      // });
      //
      // // For each file, add it either as a real .class file or as a resource
      // for (int i = 0; i < files.length; i++) {
      // // ASSERT: files[i].getAbsolutePath().startsWith(inFile.getAbsolutePath()
      // // or we are in trouble...
      // String filename = null;
      // try {
      // filename = files[i].getCanonicalPath().substring(dir.getCanonicalPath().length() + 1);
      // } catch (IOException e) {
      // // we are in trouble if this happens...
      // IMessage msg = new Message("call to getCanonicalPath() failed for file " + files[i] + " with: " + e.getMessage(),
      // new SourceLocation(files[i], 0), false);
      // buildManager.handler.handleMessage(msg);
      // filename = files[i].getAbsolutePath().substring(dir.getAbsolutePath().length() + 1);
      // }
      //
      // maybeDeleteResource(filename, oldResources);
      // }
      // }

      private void deleteClassFile(ClassFile cf) {
            classesFromName.remove(cf.fullyQualifiedTypeName);
            weaver.deleteClassFile(cf.fullyQualifiedTypeName);
            cf.deleteFromFileSystem(buildConfig);
      }

      private UnwovenClassFile createUnwovenClassFile(AjBuildConfig.BinarySourceFile bsf) {
            UnwovenClassFile ucf = null;
            try {
                  File outputDir = buildConfig.getOutputDir();
                  if (buildConfig.getCompilationResultDestinationManager() != null) {
                        // createUnwovenClassFile is called only for classes that are on the inpath,
                        // all inpath classes are put in the defaultOutputLocation, therefore,
                        // this is the output dir
                        outputDir = buildConfig.getCompilationResultDestinationManager().getDefaultOutputLocation();
                  }
                  ucf = weaver.addClassFile(bsf.binSrc, bsf.fromInPathDirectory, outputDir);
            } catch (IOException ex) {
                  IMessage msg = new Message("can't read class file " + bsf.binSrc.getPath(), new SourceLocation(bsf.binSrc, 0), false);
                  buildManager.handler.handleMessage(msg);
            }
            return ucf;
      }

      public void noteResult(InterimCompilationResult result) {
            if (!maybeIncremental()) {
                  return;
            }

            File sourceFile = new File(result.fileName());
            CompilationResult cr = result.result();

            references.put(sourceFile, new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));

            UnwovenClassFile[] unwovenClassFiles = result.unwovenClassFiles();
            for (int i = 0; i < unwovenClassFiles.length; i++) {
                  File lastTimeRound = (File) classesFromName.get(unwovenClassFiles[i].getClassName());
                  recordClassFile(unwovenClassFiles[i], lastTimeRound);
                  classesFromName.put(unwovenClassFiles[i].getClassName(), new File(unwovenClassFiles[i].getFilename()));
            }

            // need to do this before types are deleted from the World...
            recordWhetherCompilationUnitDefinedAspect(sourceFile, cr);
            deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(sourceFile, unwovenClassFiles);

            recordFQNsResultingFromCompilationUnit(sourceFile, result);
      }

      public void noteNewResult(CompilationResult cr) {
            // if (!maybeIncremental()) {
            // return;
            // }
            //
            // // File sourceFile = new File(result.fileName());
            // // CompilationResult cr = result.result();
            // if (new String(cr.getFileName()).indexOf("C") != -1) {
            // cr.references.put(new String(cr.getFileName()),
            // new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
            // int stop = 1;
            // }

            // references.put(sourceFile, new ReferenceCollection(cr.qualifiedReferences, cr.simpleNameReferences));
            //
            // UnwovenClassFile[] unwovenClassFiles = cr.unwovenClassFiles();
            // for (int i = 0; i < unwovenClassFiles.length; i++) {
            // File lastTimeRound = (File) classesFromName.get(unwovenClassFiles[i].getClassName());
            // recordClassFile(unwovenClassFiles[i], lastTimeRound);
            // classesFromName.put(unwovenClassFiles[i].getClassName(), new File(unwovenClassFiles[i].getFilename()));
            // }

            // need to do this before types are deleted from the World...
            // recordWhetherCompilationUnitDefinedAspect(sourceFile, cr);
            // deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(sourceFile, unwovenClassFiles);
            //
            // recordFQNsResultingFromCompilationUnit(sourceFile, result);
      }

      /**
       * @param sourceFile
       * @param unwovenClassFiles
       */
01315       private void deleteTypesThatWereInThisCompilationUnitLastTimeRoundButHaveBeenDeletedInThisIncrement(File sourceFile,
                  UnwovenClassFile[] unwovenClassFiles) {
            List classFiles = (List) this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(sourceFile);
            if (classFiles != null) {
                  for (int i = 0; i < unwovenClassFiles.length; i++) {
                        // deleting also deletes types from the weaver... don't do this if they are
                        // still present this time around...
                        removeFromClassFilesIfPresent(unwovenClassFiles[i].getClassName(), classFiles);
                  }
                  for (Iterator iter = classFiles.iterator(); iter.hasNext();) {
                        ClassFile cf = (ClassFile) iter.next();
                        deleteClassFile(cf);
                  }
            }
      }

      private void removeFromClassFilesIfPresent(String className, List classFiles) {
            ClassFile victim = null;
            for (Iterator iter = classFiles.iterator(); iter.hasNext();) {
                  ClassFile cf = (ClassFile) iter.next();
                  if (cf.fullyQualifiedTypeName.equals(className)) {
                        victim = cf;
                        break;
                  }
            }
            if (victim != null) {
                  classFiles.remove(victim);
            }
      }

      /**
       * Record the fully-qualified names of the types that were declared in the given source file.
       * 
       * @param sourceFile, the compilation unit
       * @param icr, the CompilationResult from compiling it
       */
01351       private void recordFQNsResultingFromCompilationUnit(File sourceFile, InterimCompilationResult icr) {
            List classFiles = new ArrayList();
            UnwovenClassFile[] types = icr.unwovenClassFiles();
            for (int i = 0; i < types.length; i++) {
                  classFiles.add(new ClassFile(types[i].getClassName(), new File(types[i].getFilename())));
            }
            this.fullyQualifiedTypeNamesResultingFromCompilationUnit.put(sourceFile, classFiles);
      }

      /**
       * If this compilation unit defined an aspect, we need to know in case it is modified in a future increment.
       * 
       * @param sourceFile
       * @param cr
       */
01366       private void recordWhetherCompilationUnitDefinedAspect(File sourceFile, CompilationResult cr) {
            this.sourceFilesDefiningAspects.remove(sourceFile);

            if (cr != null) {
                  Map compiledTypes = cr.compiledTypes;
                  if (compiledTypes != null) {
                        for (Iterator iterator = compiledTypes.keySet().iterator(); iterator.hasNext();) {
                              char[] className = (char[]) iterator.next();
                              String typeName = new String(className).replace('/', '.');
                              if (typeName.indexOf(BcelWeaver.SYNTHETIC_CLASS_POSTFIX) == -1) {
                                    ResolvedType rt = world.resolve(typeName);
                                    if (rt.isMissing()) {
                                          // This can happen in a case where another problem has occurred that prevented it being
                                          // correctly added to the world. Eg. pr148285. Duplicate types
                                          // throw new IllegalStateException("Type '" + rt.getSignature() + "' not found in world!");
                                    } else if (rt.isAspect()) {
                                          this.sourceFilesDefiningAspects.add(sourceFile);
                                          break;
                                    }
                              }
                        }
                  }
            }

      }

      // private UnwovenClassFile removeFromPreviousIfPresent(UnwovenClassFile cf, InterimCompilationResult previous) {
      // if (previous == null)
      // return null;
      // UnwovenClassFile[] unwovenClassFiles = previous.unwovenClassFiles();
      // for (int i = 0; i < unwovenClassFiles.length; i++) {
      // UnwovenClassFile candidate = unwovenClassFiles[i];
      // if ((candidate != null) && candidate.getFilename().equals(cf.getFilename())) {
      // unwovenClassFiles[i] = null;
      // return candidate;
      // }
      // }
      // return null;
      // }

      private void recordClassFile(UnwovenClassFile thisTime, File lastTime) {
            if (simpleStrings == null) {
                  // batch build
                  // record resolved type for structural comparisions in future increments
                  // this records a second reference to a structure already held in memory
                  // by the world.
                  ResolvedType rType = world.resolve(thisTime.getClassName());
                  if (!rType.isMissing()) {
                        try {
                              ClassFileReader reader = new ClassFileReader(thisTime.getBytes(), null);
                              this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(), new CompactTypeStructureRepresentation(
                                          reader));
                        } catch (ClassFormatException cfe) {
                              throw new BCException("Unexpected problem processing class", cfe);
                        }
                  }
                  return;
            }

            CompactTypeStructureRepresentation existingStructure = (CompactTypeStructureRepresentation) this.resolvedTypeStructuresFromLastBuild
                        .get(thisTime.getClassName());
            ResolvedType newResolvedType = world.resolve(thisTime.getClassName());
            if (!newResolvedType.isMissing()) {
                  try {
                        ClassFileReader reader = new ClassFileReader(thisTime.getBytes(), null);
                        this.resolvedTypeStructuresFromLastBuild.put(thisTime.getClassName(),
                                    new CompactTypeStructureRepresentation(reader));
                  } catch (ClassFormatException cfe) {
                        throw new BCException("Unexpected problem processing class", cfe);
                  }
            }

            if (lastTime == null) {
                  recordTypeChanged(thisTime.getClassName());
                  return;
            }

            if (newResolvedType.isMissing()) {
                  return;
            }
            world.ensureAdvancedConfigurationProcessed();
            byte[] newBytes = thisTime.getBytes();
            try {
                  ClassFileReader reader = new ClassFileReader(newBytes, lastTime.getAbsolutePath().toCharArray());
                  // ignore local types since they're only visible inside a single method
                  if (!(reader.isLocal() || reader.isAnonymous())) {
                        if (hasStructuralChanges(reader, existingStructure)) {
                              if (world.forDEBUG_structuralChangesCode)
                                    System.err.println("Detected a structural change in " + thisTime.getFilename());
                              structuralChangesSinceLastFullBuild.put(thisTime.getFilename(), new Long(currentBuildTime));
                              recordTypeChanged(new String(reader.getName()).replace('/', '.'));
                        }
                  }
            } catch (ClassFormatException e) {
                  recordTypeChanged(thisTime.getClassName());
            }
      }

      private static final char[][] EMPTY_CHAR_ARRAY = new char[0][];

      /**
       * Compare the class structure of the new intermediate (unwoven) class with the existingResolvedType of the same class that we
       * have in the world, looking for any structural differences (and ignoring aj members resulting from weaving....)
       * 
       * Some notes from Andy... lot of problems here, which I've eventually resolved by building the compactstructure based on a
       * classfilereader, rather than on a ResolvedType. There are accessors for inner types and funky fields that the compiler
       * creates to support the language - for non-static inner types it also mangles ctors to be prefixed with an instance of the
       * surrounding type.
       * 
       * Warning : long but boring method implementation...
       * 
       * @param reader
       * @param existingType
       * @return
       */
01481       private boolean hasStructuralChanges(ClassFileReader reader, CompactTypeStructureRepresentation existingType) {
            if (existingType == null) {
                  return true;
            }

            // modifiers
            if (!modifiersEqual(reader.getModifiers(), existingType.modifiers)) {
                  return true;
            }

            // generic signature
            if (!CharOperation.equals(reader.getGenericSignature(), existingType.genericSignature)) {
                  return true;
            }

            // superclass name
            if (!CharOperation.equals(reader.getSuperclassName(), existingType.superclassName)) {
                  return true;
            }

            // have annotations changed on the type?
            IBinaryAnnotation[] newAnnos = reader.getAnnotations();
            if (newAnnos == null || newAnnos.length == 0) {
                  if (existingType.annotations != null && existingType.annotations.length != 0) {
                        return true;
                  }
            } else {
                  IBinaryAnnotation[] existingAnnos = existingType.annotations;
                  if (existingAnnos == null || existingAnnos.length != newAnnos.length) {
                        return true;
                  }
                  // Does not allow for an order switch
                  // Does not cope with a change in values set on the annotation (hard to create a testcase where this is a problem tho)
                  for (int i = 0; i < newAnnos.length; i++) {
                        if (!CharOperation.equals(newAnnos[i].getTypeName(), existingAnnos[i].getTypeName())) {
                              return true;
                        }
                  }

            }

            // interfaces
            char[][] existingIfs = existingType.interfaces;
            char[][] newIfsAsChars = reader.getInterfaceNames();
            if (newIfsAsChars == null) {
                  newIfsAsChars = EMPTY_CHAR_ARRAY;
            } // damn I'm lazy...
            if (existingIfs == null) {
                  existingIfs = EMPTY_CHAR_ARRAY;
            }
            if (existingIfs.length != newIfsAsChars.length)
                  return true;
            new_interface_loop: for (int i = 0; i < newIfsAsChars.length; i++) {
                  for (int j = 0; j < existingIfs.length; j++) {
                        if (CharOperation.equals(existingIfs[j], newIfsAsChars[i])) {
                              continue new_interface_loop;
                        }
                  }
                  return true;
            }

            // fields
            // CompactMemberStructureRepresentation[] existingFields = existingType.fields;
            IBinaryField[] newFields = reader.getFields();
            if (newFields == null) {
                  newFields = CompactTypeStructureRepresentation.NoField;
            }

            // all redundant for now ... could be an optimization at some point...
            // remove any ajc$XXX fields from those we compare with
            // the existing fields - bug 129163
            // List nonGenFields = new ArrayList();
            // for (int i = 0; i < newFields.length; i++) {
            // IBinaryField field = newFields[i];
            // //if (!CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,field.getName())) { // this would skip ajc$ fields
            // //if ((field.getModifiers()&0x1000)==0) // 0x1000 => synthetic - this will skip synthetic fields (eg. this$0)
            // nonGenFields.add(field);
            // //}
            // }
            IBinaryField[] existingFs = existingType.binFields;
            if (newFields.length != existingFs.length)
                  return true;
            new_field_loop: for (int i = 0; i < newFields.length; i++) {
                  IBinaryField field = newFields[i];
                  char[] fieldName = field.getName();
                  for (int j = 0; j < existingFs.length; j++) {
                        if (CharOperation.equals(existingFs[j].getName(), fieldName)) {
                              if (!modifiersEqual(field.getModifiers(), existingFs[j].getModifiers())) {
                                    return true;
                              }
                              if (!CharOperation.equals(existingFs[j].getTypeName(), field.getTypeName())) {
                                    return true;
                              }
                              continue new_field_loop;
                        }
                  }
                  return true;
            }

            // methods
            // CompactMemberStructureRepresentation[] existingMethods = existingType.methods;
            IBinaryMethod[] newMethods = reader.getMethods();
            if (newMethods == null) {
                  newMethods = CompactTypeStructureRepresentation.NoMethod;
            }

            // all redundant for now ... could be an optimization at some point...

            // Ctors in a non-static inner type have an 'extra parameter' of the enclosing type.
            // If skippableDescriptorPrefix gets set here then it is set to the descriptor portion
            // for this 'extra parameter'. For an inner class of pkg.Foo the skippable descriptor
            // prefix will be '(Lpkg/Foo;' - so later when comparing <init> methods we know what to
            // compare.
            // IF THIS CODE NEEDS TO GET MORE COMPLICATED, I THINK ITS WORTH RIPPING IT ALL OUT AND
            // CREATING THE STRUCTURAL CHANGES OBJECT BASED ON CLASSREADER OUTPUT RATHER THAN
            // THE RESOLVEDTYPE - THEN THERE WOULD BE NO NEED TO TREAT SOME METHODS IN A PECULIAR
            // WAY.
            // char[] skippableDescriptorPrefix = null;
            // char[] enclosingTypeName = reader.getEnclosingTypeName();
            // boolean isStaticType = Modifier.isStatic(reader.getModifiers());
            // if (!isStaticType && enclosingTypeName!=null) {
            // StringBuffer sb = new StringBuffer();
            // sb.append("(L").append(new String(enclosingTypeName)).append(";");
            // skippableDescriptorPrefix = sb.toString().toCharArray();
            // }
            //          
            //          
            // // remove the aspectOf, hasAspect, clinit and ajc$XXX methods
            // // from those we compare with the existing methods - bug 129163
            // List nonGenMethods = new ArrayList();
            // for (int i = 0; i < newMethods.length; i++) {
            // IBinaryMethod method = newMethods[i];
            // // if ((method.getModifiers() & 0x1000)!=0) continue; // 0x1000 => synthetic - will cause us to skip access$0 - is this
            // always safe?
            // char[] methodName = method.getSelector();
            // // if (!CharOperation.equals(methodName,NameMangler.METHOD_ASPECTOF) &&
            // // !CharOperation.equals(methodName,NameMangler.METHOD_HASASPECT) &&
            // // !CharOperation.equals(methodName,NameMangler.STATIC_INITIALIZER) &&
            // // !CharOperation.prefixEquals(NameMangler.AJC_DOLLAR_PREFIX,methodName) &&
            // // !CharOperation.prefixEquals(NameMangler.CLINIT,methodName)) {
            // nonGenMethods.add(method);
            // // }
            // }
            IBinaryMethod[] existingMs = existingType.binMethods;
            if (newMethods.length != existingMs.length)
                  return true;
            new_method_loop: for (int i = 0; i < newMethods.length; i++) {
                  IBinaryMethod method = newMethods[i];
                  char[] methodName = method.getSelector();
                  for (int j = 0; j < existingMs.length; j++) {
                        if (CharOperation.equals(existingMs[j].getSelector(), methodName)) {
                              // candidate match
                              if (!CharOperation.equals(method.getMethodDescriptor(), existingMs[j].getMethodDescriptor())) {
                                    // ok, the descriptors don't match, but is this a funky ctor on a non-static inner
                                    // type?
                                    // boolean mightBeOK =
                                    // skippableDescriptorPrefix!=null && // set for inner types
                                    // CharOperation.equals(methodName,NameMangler.INIT) && // ctor
                                    // CharOperation.prefixEquals(skippableDescriptorPrefix,method.getMethodDescriptor()); // checking for
                                    // prefix on the descriptor
                                    // if (mightBeOK) {
                                    // // OK, so the descriptor starts something like '(Lpkg/Foo;' - we now may need to look at the rest of the
                                    // // descriptor if it takes >1 parameter.
                                    // // eg. could be (Lpkg/C;Ljava/lang/String;) where the skippablePrefix is (Lpkg/C;
                                    // char [] md = method.getMethodDescriptor();
                                    // char[] remainder = CharOperation.subarray(md, skippableDescriptorPrefix.length, md.length);
                                    // if (CharOperation.equals(remainder,BRACKET_V)) continue new_method_loop; // no other parameters to worry
                                    // about
                                    // char[] comparableSig = CharOperation.subarray(existingMethods[j].signature, 1,
                                    // existingMethods[j].signature.length);
                                    // boolean match = CharOperation.equals(comparableSig, remainder);
                                    // if (match) continue new_method_loop;
                                    // }
                                    continue; // might be overloading
                              } else {
                                    // matching sigs
                                    if (!modifiersEqual(method.getModifiers(), existingMs[j].getModifiers())) {
                                          return true;
                                    }
                                    continue new_method_loop;
                              }
                        }
                  }
                  return true; // (no match found)
            }

            return false;
      }

      private boolean modifiersEqual(int eclipseModifiers, int resolvedTypeModifiers) {
            resolvedTypeModifiers = resolvedTypeModifiers & ExtraCompilerModifiers.AccJustFlag;
            eclipseModifiers = eclipseModifiers & ExtraCompilerModifiers.AccJustFlag;
            // if ((eclipseModifiers & CompilerModifiers.AccSuper) != 0) {
            // eclipseModifiers -= CompilerModifiers.AccSuper;
            // }
            return (eclipseModifiers == resolvedTypeModifiers);
      }

      // private static StringSet makeStringSet(List strings) {
      // StringSet ret = new StringSet(strings.size());
      // for (Iterator iter = strings.iterator(); iter.hasNext();) {
      // String element = (String) iter.next();
      // ret.add(element);
      // }
      // return ret;
      // }

      private String stringifyList(Set l) {
            StringBuffer sb = new StringBuffer();
            sb.append("{");
            for (Iterator iter = l.iterator(); iter.hasNext();) {
                  Object el = iter.next();
                  sb.append(el);
                  if (iter.hasNext())
                        sb.append(",");
            }
            sb.append("}");
            return sb.toString();
      }

      protected void addAffectedSourceFiles(Set addTo, Set lastTimeSources) {
            if (qualifiedStrings.elementSize == 0 && simpleStrings.elementSize == 0)
                  return;
            if (listenerDefined())
                  getListener().recordDecision(
                              "Examining whether any other files now need compilation based on just compiling: '"
                                          + stringifyList(lastTimeSources) + "'");
            // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
            char[][][] qualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedStrings);
            // if a well known qualified name was found then we can skip over these
            if (qualifiedNames.length < qualifiedStrings.elementSize)
                  qualifiedNames = null;
            char[][] simpleNames = ReferenceCollection.internSimpleNames(simpleStrings);
            // if a well known name was found then we can skip over these
            if (simpleNames.length < simpleStrings.elementSize)
                  simpleNames = null;

            // System.err.println("simple: " + simpleStrings);
            // System.err.println("qualif: " + qualifiedStrings);

            for (Iterator i = references.entrySet().iterator(); i.hasNext();) {
                  Map.Entry entry = (Map.Entry) i.next();
                  ReferenceCollection refs = (ReferenceCollection) entry.getValue();
                  if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
                        File file = (File) entry.getKey();
                        if (file.exists()) {
                              if (!lastTimeSources.contains(file)) { // ??? O(n**2)
                                    if (listenerDefined()) {
                                          getListener().recordDecision("Need to recompile '" + file.getName().toString() + "'");
                                    }
                                    addTo.add(file);
                              }
                        }
                  }
            }
            // add in the things we compiled previously - I know that seems crap but otherwise we may pull woven
            // stuff off disk (since we no longer have UnwovenClassFile objects) in order to satisfy references
            // in the new files we are about to compile (see pr133532)
            if (addTo.size() > 0) {
                  addTo.addAll(lastTimeSources);
            }
            // // XXX Promote addTo to a Set - then we don't need this rubbish? but does it need to be ordered?
            // if (addTo.size()>0) {
            // for (Iterator iter = lastTimeSources.iterator(); iter.hasNext();) {
            // Object element = (Object) iter.next();
            // if (!addTo.contains(element)) addTo.add(element);
            // }
            // }

            qualifiedStrings.clear();
            simpleStrings.clear();
      }

      /**
       * Record that a particular type has been touched during a compilation run. Information is used to ensure any types depending
       * upon this one are also recompiled.
       * 
       * @param typename (possibly qualified) type name
       */
01760       protected void recordTypeChanged(String typename) {
            int lastDot = typename.lastIndexOf('.');
            String typeName;
            if (lastDot != -1) {
                  String packageName = typename.substring(0, lastDot).replace('.', '/');
                  qualifiedStrings.add(packageName);
                  typeName = typename.substring(lastDot + 1);
            } else {
                  qualifiedStrings.add("");
                  typeName = typename;
            }

            int memberIndex = typeName.indexOf('$');
            if (memberIndex > 0)
                  typeName = typeName.substring(0, memberIndex);
            simpleStrings.add(typeName);
      }

      protected void addDependentsOf(File sourceFile) {
            List cfs = (List) this.fullyQualifiedTypeNamesResultingFromCompilationUnit.get(sourceFile);

            if (cfs != null) {
                  for (Iterator iter = cfs.iterator(); iter.hasNext();) {
                        ClassFile cf = (ClassFile) iter.next();
                        recordTypeChanged(cf.fullyQualifiedTypeName);
                  }
            }

      }

      public void setStructureModel(AsmManager structureModel) {
            this.structureModel = structureModel;
      }

      public AsmManager getStructureModel() {
            return structureModel;
      }

      public void setWeaver(BcelWeaver bw) {
            weaver = bw;
      }

      public BcelWeaver getWeaver() {
            return weaver;
      }

      public void setWorld(BcelWorld bw) {
            world = bw;
      }

      public BcelWorld getBcelWorld() {
            return world;
      }

      //
      // public void setRelationshipMap(IRelationshipMap irm) {
      // relmap = irm;
      // }
      //
      // public IRelationshipMap getRelationshipMap() {
      // return relmap;
      // }

      public int getNumberOfStructuralChangesSinceLastFullBuild() {
            return structuralChangesSinceLastFullBuild.size();
      }

      /** Returns last time we did a full or incremental build. */
01828       public long getLastBuildTime() {
            return lastSuccessfulBuildTime;
      }

      /** Returns last time we did a full build */
01833       public long getLastFullBuildTime() {
            return lastSuccessfulFullBuildTime;
      }

      /**
       * @return Returns the buildConfig.
       */
01840       public AjBuildConfig getBuildConfig() {
            return this.buildConfig;
      }

      public void clearBinarySourceFiles() {
            this.binarySourceFiles = new HashMap();
      }

      public void recordBinarySource(String fromPathName, List unwovenClassFiles) {
            this.binarySourceFiles.put(fromPathName, unwovenClassFiles);
            if (this.maybeIncremental()) {
                  List simpleClassFiles = new LinkedList();
                  for (Iterator iter = unwovenClassFiles.iterator(); iter.hasNext();) {
                        UnwovenClassFile ucf = (UnwovenClassFile) iter.next();
                        ClassFile cf = getClassFileFor(ucf);
                        simpleClassFiles.add(cf);
                  }
                  this.inputClassFilesBySource.put(fromPathName, simpleClassFiles);
            }
      }

      /**
       * @param ucf
       * @return
       */
01865       private ClassFile getClassFileFor(UnwovenClassFile ucf) {
            return new ClassFile(ucf.getClassName(), new File(ucf.getFilename()));
      }

      public Map getBinarySourceMap() {
            return this.binarySourceFiles;
      }

      public Map getClassNameToFileMap() {
            return this.classesFromName;
      }

      public boolean hasResource(String resourceName) {
            return this.resources.keySet().contains(resourceName);
      }

      public void recordResource(String resourceName, File resourceSourceLocation) {
            this.resources.put(resourceName, resourceSourceLocation);
      }

      /**
       * @return Returns the addedFiles.
       */
01888       public Set getAddedFiles() {
            return this.addedFiles;
      }

      /**
       * @return Returns the deletedFiles.
       */
01895       public Set getDeletedFiles() {
            return this.deletedFiles;
      }

      public void forceBatchBuildNextTimeAround() {
            this.batchBuildRequiredThisTime = true;
      }

      public boolean requiresFullBatchBuild() {
            return this.batchBuildRequiredThisTime;
      }

      private static class ClassFile {
            public String fullyQualifiedTypeName;
            public File locationOnDisk;

            public ClassFile(String fqn, File location) {
                  this.fullyQualifiedTypeName = fqn;
                  this.locationOnDisk = location;
            }

            public void deleteFromFileSystem(AjBuildConfig buildConfig) {
                  String namePrefix = locationOnDisk.getName();
                  namePrefix = namePrefix.substring(0, namePrefix.lastIndexOf('.'));
                  final String targetPrefix = namePrefix + BcelWeaver.CLOSURE_CLASS_PREFIX;
                  File dir = locationOnDisk.getParentFile();
                  if (dir != null) {
                        File[] weaverGenerated = dir.listFiles(new FilenameFilter() {
                              public boolean accept(File dir, String name) {
                                    return name.startsWith(targetPrefix);
                              }
                        });
                        if (weaverGenerated != null) {
                              for (int i = 0; i < weaverGenerated.length; i++) {
                                    weaverGenerated[i].delete();
                                    if (buildConfig != null && buildConfig.getCompilationResultDestinationManager() != null) {
                                          buildConfig.getCompilationResultDestinationManager().reportFileRemove(weaverGenerated[i].getPath(),
                                                      CompilationResultDestinationManager.FILETYPE_CLASS);
                                    }
                              }
                        }
                  }
                  locationOnDisk.delete();
                  if (buildConfig != null && buildConfig.getCompilationResultDestinationManager() != null) {
                        buildConfig.getCompilationResultDestinationManager().reportFileRemove(locationOnDisk.getPath(),
                                    CompilationResultDestinationManager.FILETYPE_CLASS);
                  }
            }
      }

      public void wipeAllKnowledge() {
            buildManager.state = null;
            // buildManager.setStructureModel(null);
      }

      public Map getAspectNamesToFileNameMap() {
            return aspectsFromFileNames;
      }

      public void initializeAspectNamesToFileNameMap() {
            this.aspectsFromFileNames = new HashMap();
      }

      // Will allow us to record decisions made during incremental processing, hopefully aid in debugging
      public boolean listenerDefined() {
            return stateListener != null;
      }

      public IStateListener getListener() {
            return stateListener;
      }

      public IBinaryType checkPreviousBuild(String name) {
            return (IBinaryType) resolvedTypeStructuresFromLastBuild.get(name);
      }

      public AjBuildManager getAjBuildManager() {
            return buildManager;
      }

      public INameEnvironment getNameEnvironment() {
            return this.nameEnvironment;
      }

      public void setNameEnvironment(INameEnvironment nameEnvironment) {
            this.nameEnvironment = nameEnvironment;
      }

      /**
       * Record an aspect that came in on the aspect path. When a .class file changes on the aspect path we can then recognize it as
       * an aspect and know to do more than just a tiny incremental build. <br>
       * TODO but this doesn't allow for a new aspect created on the aspectpath?
       * 
       * @param aspectFile path to the file, eg. c:/temp/foo/Fred.class
       */
01990       public void recordAspectClassFile(String aspectFile) {
            aspectClassFiles.add(aspectFile);
      }
}

Generated by  Doxygen 1.6.0   Back to index