/*******************************************************************************
 * Copyright (c) 2006 IBM Corporation and others.
 * 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:
 *     Andy Clement    - initial implementation 26Jul06
 *******************************************************************************/
package org.aspectj.ajdt.internal.compiler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.aspectj.ajdt.internal.compiler.ast.AddAtAspectJAnnotationsVisitor;
import org.aspectj.ajdt.internal.compiler.ast.AspectDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.InterTypeConstructorDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.InterTypeFieldDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.InterTypeMethodDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.ValidateAtAspectJAnnotationsVisitor;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.ajdt.internal.core.builder.AjState;
import org.aspectj.asm.internal.CharOperation;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.IMessageHandler;
import org.aspectj.bridge.IProgressListener;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
import org.aspectj.org.eclipse.jdt.internal.compiler.Compiler;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.aspectj.weaver.bcel.BcelWeaver;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.bcel.UnwovenClassFile;

/**
 * Adapts standard JDT Compiler to add in AspectJ specific behaviours. This version implements pipelining - where files are compiled
 * and then woven immediately, unlike AjCompilerAdapter which compiles everything then weaves everything. (One small note: because
 * all aspects have to be known before weaving can take place, the weaving pipeline is 'stalled' until all aspects have been
 * compiled).
 * 
 * The basic strategy is this:
 * 
 * 1. diet parse all input source files - this is enough for us to implement ITD matching - this enables us to determine which are
 * aspects 2. sort the input files, aspects first - keep a note of how many files contain aspects 3. if there are aspects, mark the
 * pipeline as 'stalled' 3. repeat 3a. compile a file 3b. have we now compiled all aspects? NO - put file in a weave pending queue
 * YES- unstall the 'pipeline' 3c. is the pipeline stalled? NO - weave all pending files and this one YES- do nothing
 * 
 * Complexities arise because of: - what does -XterminateAfterCompilation mean? since there is no stage where everything is compiled
 * and nothing is woven
 * 
 * 
 * Here is the compiler loop difference when pipelining.
 * 
 * the old way: Finished diet parsing [C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java] Finished diet parsing
 * [C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java] > AjLookupEnvironment.completeTypeBindings() <
 * AjLookupEnvironment.completeTypeBindings() compiling C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java
 * >Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java)
 * <Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java) compiling
 * C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java
 * >Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java)
 * <Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java) >AjCompilerAdapter.weave()
 * >BcelWeaver.prepareForWeave <BcelWeaver.prepareForWeave woven class ClassOne (from
 * C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java) woven class ClassTwo (from
 * C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java) <AjCompilerAdapter.weave()
 * 
 * the new way (see the compiling/weaving mixed up): Finished diet parsing
 * [C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java] Finished diet parsing
 * [C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java] >AjLookupEnvironment.completeTypeBindings()
 * <AjLookupEnvironment.completeTypeBindings() compiling C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java
 * >Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java)
 * <Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java) >AjCompilerAdapter.weave()
 * >BcelWeaver.prepareForWeave <BcelWeaver.prepareForWeave woven class ClassOne (from
 * C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassOne.java) <AjCompilerAdapter.weave() compiling
 * C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java
 * >Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java)
 * <Compiler.process(C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java) >AjCompilerAdapter.weave() woven class ClassTwo
 * (from C:\temp\ajcSandbox\aspectjhead\ajcTest23160.tmp\ClassTwo.java) <AjCompilerAdapter.weave()
 * 
 * 
 */
public class AjPipeliningCompilerAdapter extends AbstractCompilerAdapter {

	private Compiler compiler;
	private BcelWeaver weaver;
	private EclipseFactory eWorld;
	private boolean isBatchCompile;
	private boolean reportedErrors;
	private boolean isXTerminateAfterCompilation;
	private boolean proceedOnError;
	private boolean inJava5Mode;
	private boolean makeReflectable;
	private boolean noAtAspectJAnnotationProcessing;
	private IIntermediateResultsRequestor intermediateResultsRequestor;
	private IProgressListener progressListener;
	private IOutputClassFileNameProvider outputFileNameProvider;
	private IBinarySourceProvider binarySourceProvider;
	private WeaverMessageHandler weaverMessageHandler;
	private Map<String, List<UnwovenClassFile>> binarySourceSetForFullWeave = new HashMap<String, List<UnwovenClassFile>>();

	private ContextToken processingToken = null;
	private ContextToken resolvingToken = null;
	private ContextToken analysingToken = null;
	private ContextToken generatingToken = null;

	private AjState incrementalCompilationState;

	// Maintains a list of whats weaving - whilst the pipeline is stalled, this accumulates aspects.
	List<InterimCompilationResult> resultsPendingWeave = new ArrayList<InterimCompilationResult>();

	// pipelining info
	private boolean pipelineStalled = true;
	private boolean weaverInitialized = false;
	private int toWaitFor;
	// If we determine we are going to drop back to a full build - don't need to tell the weaver to report adviceDidNotMatch
	private boolean droppingBackToFullBuild;

	/**
	 * Create an adapter, and tell it everything it needs to now to drive the AspectJ parts of a compile cycle.
	 * 
	 * @param compiler the JDT compiler that produces class files from source
	 * @param isBatchCompile true if this is a full build (non-incremental)
	 * @param world the bcelWorld used for type resolution during weaving
	 * @param weaver the weaver
	 * @param intRequestor recipient of interim compilation results from compiler (pre-weave)
	 * @param outputFileNameProvider implementor of a strategy providing output file names for results
	 * @param binarySourceEntries binary source that we didn't compile, but that we need to weave
	 * @param resultSetForFullWeave if we are doing an incremental build, and the weaver determines that we need to weave the world,
	 *        this is the set of intermediate results that will be passed to the weaver.
	 */
	public AjPipeliningCompilerAdapter(Compiler compiler, boolean isBatchCompile, BcelWorld world, BcelWeaver weaver,
			EclipseFactory eFactory, IIntermediateResultsRequestor intRequestor, IProgressListener progressListener,
			IOutputClassFileNameProvider outputFileNameProvider, IBinarySourceProvider binarySourceProvider,
			Map fullBinarySourceEntries, /* fileName |-> List<UnwovenClassFile> */
			boolean isXterminateAfterCompilation, boolean proceedOnError, boolean noAtAspectJProcessing, boolean makeReflectable,
			AjState incrementalCompilationState) {
		this.compiler = compiler;
		this.isBatchCompile = isBatchCompile;
		this.weaver = weaver;
		this.intermediateResultsRequestor = intRequestor;
		this.progressListener = progressListener;
		this.outputFileNameProvider = outputFileNameProvider;
		this.binarySourceProvider = binarySourceProvider;
		this.isXTerminateAfterCompilation = isXterminateAfterCompilation;
		this.proceedOnError = proceedOnError;
		this.binarySourceSetForFullWeave = fullBinarySourceEntries;
		this.eWorld = eFactory;
		this.inJava5Mode = false;
		this.makeReflectable = makeReflectable;
		this.noAtAspectJAnnotationProcessing = noAtAspectJProcessing;
		this.incrementalCompilationState = incrementalCompilationState;

		if (compiler.options.complianceLevel >= ClassFileConstants.JDK1_5) {
			inJava5Mode = true;
		}
		IMessageHandler msgHandler = world.getMessageHandler();
		// Do we need to reset the message handler or create a new one? (This saves a ton of memory lost on incremental compiles...)
		if (msgHandler instanceof WeaverMessageHandler) {
			((WeaverMessageHandler) msgHandler).resetCompiler(compiler);
			weaverMessageHandler = (WeaverMessageHandler) msgHandler;
		} else {
			weaverMessageHandler = new WeaverMessageHandler(msgHandler, compiler);
			world.setMessageHandler(weaverMessageHandler);
		}
	}

	// the compilation lifecycle methods below are called in order as compilation progresses...

	/**
	 * In a pipelining compilation system, we need to ensure aspects are through the pipeline first. Only when they are all through
	 * (and therefore we know about all static/dynamic crosscutting) can be proceed to weave anything. Effectively the weaving part
	 * of the pipeline stalls until all the aspects have been fully compiled. This method sorts the compilation units such that any
	 * containing aspects are fully compiled first and it keeps a note on how long it should stall the pipeline before commencing
	 * weaving.
	 */
	public void afterDietParsing(CompilationUnitDeclaration[] units) {
		if (debugPipeline) {
			System.err.println("> afterDietParsing: there are " + (units == null ? 0 : units.length) + " units to sort");
		}

		if (!reportedErrors && units != null) {
			for (int i = 0; i < units.length; i++) {
				if (units[i] != null && units[i].compilationResult != null && units[i].compilationResult.hasErrors()) {
					reportedErrors = true;
					break; // TODO break or exit here?
				}
			}
		}

		// Break the units into two lists...
		List<CompilationUnitDeclaration> aspects = new ArrayList<CompilationUnitDeclaration>();
		List<CompilationUnitDeclaration> nonaspects = new ArrayList<CompilationUnitDeclaration>();
		for (int i = 0; i < units.length; i++) {
			if (containsAnAspect(units[i])) {
				aspects.add(units[i]);
			} else {
				nonaspects.add(units[i]);
			}
		}

		// ...and put them back together, aspects first
		int posn = 0;
		for (CompilationUnitDeclaration aspect : aspects) {
			units[posn++] = aspect;
		}
		for (CompilationUnitDeclaration nonaspect : nonaspects) {
			units[posn++] = nonaspect;
		}

		// Work out how long to stall the pipeline
		toWaitFor = aspects.size();
		if (debugPipeline) {
			System.err.println("< afterDietParsing: stalling pipeline for " + toWaitFor + " source files");
		}

		// TESTING
		if (pipelineTesting) {
			if (pipelineOutput == null) {
				pipelineOutput = new Hashtable();
			}
			pipelineOutput.put("filesContainingAspects", new Integer(toWaitFor).toString());
			StringBuffer order = new StringBuffer();
			order.append("[");
			for (int i = 0; i < units.length; i++) {
				if (i != 0) {
					order.append(",");
				}
				CompilationUnitDeclaration declaration = units[i];
				String filename = new String(declaration.getFileName());
				int idx = filename.lastIndexOf('/');
				if (idx > 0) {
					filename = filename.substring(idx + 1);
				}
				idx = filename.lastIndexOf('\\');
				if (idx > 0) {
					filename = filename.substring(idx + 1);
				}
				order.append(filename);
			}
			order.append("]");
			pipelineOutput.put("weaveOrder", order.toString());
		}
	}

	public void beforeCompiling(ICompilationUnit[] sourceUnits) {
		resultsPendingWeave = new ArrayList<InterimCompilationResult>();
		reportedErrors = false;
		droppingBackToFullBuild = false;
	}

	public void beforeProcessing(CompilationUnitDeclaration unit) {
		if (debugPipeline) {
			System.err.println("compiling " + new String(unit.getFileName()));
		}
		eWorld.showMessage(IMessage.INFO, "compiling " + new String(unit.getFileName()), null, null);
		processingToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_COMPILATION_UNIT, unit
				.getFileName());
		if (inJava5Mode && !noAtAspectJAnnotationProcessing) {
			ContextToken tok = CompilationAndWeavingContext.enteringPhase(
					CompilationAndWeavingContext.ADDING_AT_ASPECTJ_ANNOTATIONS, unit.getFileName());
			AddAtAspectJAnnotationsVisitor atAspectJVisitor = new AddAtAspectJAnnotationsVisitor(unit, makeReflectable);
			unit.traverse(atAspectJVisitor, unit.scope);
			CompilationAndWeavingContext.leavingPhase(tok);
		}
	}

	public void beforeResolving(CompilationUnitDeclaration unit) {
		resolvingToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.RESOLVING_COMPILATION_UNIT, unit
				.getFileName());
	}

	public void afterResolving(CompilationUnitDeclaration unit) {
		if (resolvingToken != null) {
			CompilationAndWeavingContext.leavingPhase(resolvingToken);
		}
	}

	public void beforeAnalysing(CompilationUnitDeclaration unit) {
		analysingToken = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.ANALYSING_COMPILATION_UNIT, unit
				.getFileName());
		if (inJava5Mode && !noAtAspectJAnnotationProcessing) {
			ValidateAtAspectJAnnotationsVisitor atAspectJVisitor = new ValidateAtAspectJAnnotationsVisitor(unit);
			unit.traverse(atAspectJVisitor, unit.scope);
		}
	}

	public void afterAnalysing(CompilationUnitDeclaration unit) {
		if (analysingToken != null) {
			CompilationAndWeavingContext.leavingPhase(analysingToken);
		}
	}

	public void beforeGenerating(CompilationUnitDeclaration unit) {
		generatingToken = CompilationAndWeavingContext.enteringPhase(
				CompilationAndWeavingContext.GENERATING_UNWOVEN_CODE_FOR_COMPILATION_UNIT, unit.getFileName());
		if (eWorld.pushinCollector != null) {
			if (unit.types != null && unit.types.length > 0) {
				for (int t = 0; t < unit.types.length; t++) {
					TypeDeclaration type = unit.types[t];
					if (type.methods != null) {
						for (int m = 0; m < type.methods.length; m++) {
							AbstractMethodDeclaration md = type.methods[m];
							if (md instanceof InterTypeMethodDeclaration) {
								InterTypeMethodDeclaration itmd = ((InterTypeMethodDeclaration) md);
								ITDMethodPrinter printer = new ITDMethodPrinter(itmd, md.scope);
								String s = printer.print();
								eWorld.pushinCollector.recordInterTypeMethodDeclarationCode(md, s, getDeclarationLineNumber(md));
							} else if (md instanceof InterTypeFieldDeclaration) {
								ITDFieldPrinter printer = new ITDFieldPrinter(((InterTypeFieldDeclaration) md), md.scope);
								String s = printer.print();
								eWorld.pushinCollector.recordInterTypeFieldDeclarationCode(md, s, getDeclarationLineNumber(md));
							} else if (md instanceof InterTypeConstructorDeclaration) {
								ITDConstructorPrinter printer = new ITDConstructorPrinter(((InterTypeConstructorDeclaration) md),
										md.scope);
								String s = printer.print();
								eWorld.pushinCollector.recordInterTypeConstructorDeclarationCode(md, s,
										getDeclarationLineNumber(md));
								// } else if (md instanceof DeclareAnnotationDeclaration) {
								// DeclareAnnotationDeclaration dad = (DeclareAnnotationDeclaration) md;
								// String value = new DeclareAnnotationsPrinter(dad, dad.scope).print();
								// eWorld.pushinCollector.recordDeclareAnnotationDeclarationCode(md, value);
							}
						}
					}
				}
			}
			eWorld.pushinCollector.setOutputFileNameProvider(outputFileNameProvider);
		}
	}

	/**
	 * @return the line number for this declaration in the source code
	 */
	private int getDeclarationLineNumber(AbstractMethodDeclaration md) {
		int sourceStart = md.sourceStart;
		int[] separators = md.compilationResult.lineSeparatorPositions;
		int declarationStartLine = 1;
		for (int i = 0; i < separators.length; i++) {
			if (sourceStart < separators[i]) {
				break;
			}
			declarationStartLine++;
		}
		return declarationStartLine;
	}

	public void afterGenerating(CompilationUnitDeclaration unit) {
		if (generatingToken != null) {
			CompilationAndWeavingContext.leavingPhase(generatingToken);
		}
		if (eWorld.pushinCollector != null) {
			eWorld.pushinCollector.dump(unit);
		}
	}

	public void afterCompiling(CompilationUnitDeclaration[] units) {
		this.eWorld.cleanup();
		if (!weaverInitialized) { // nothing got compiled, doesnt mean there is nothing to do... (binary weaving)
			if (!(isXTerminateAfterCompilation || (reportedErrors && !proceedOnError))) {
				// acceptResult(unit.compilationResult);
				// } else {
				try {
					if (weaveQueuedEntries()) {
						droppingBackToFullBuild = true;
					}
				} catch (IOException ex) {
					AbortCompilation ac = new AbortCompilation(null, ex);
					throw ac;
				}
			}
		}
		postWeave();
		try {
			// not great ... but one more check before we continue, see pr132314
			if (!reportedErrors && units != null) {
				for (int i = 0; i < units.length; i++) {
					if (units[i] != null && units[i].compilationResult != null && units[i].compilationResult.hasErrors()) {
						reportedErrors = true;
						break;
					}
				}
			}
			if (isXTerminateAfterCompilation || (reportedErrors && !proceedOnError)) {
				// no point weaving... just tell the requestor we're done
				notifyRequestor();
			} else {
				// weave(); // notification happens as weave progresses...
				// weaver.getWorld().flush(); // pr152257
			}
			// } catch (IOException ex) {
			// AbortCompilation ac = new AbortCompilation(null,ex);
			// throw ac;
		} catch (RuntimeException rEx) {
			if (rEx instanceof AbortCompilation) {
				throw rEx; // Don't wrap AbortCompilation exceptions!
			}

			// This will be unwrapped in Compiler.handleInternalException() and the nested
			// RuntimeException thrown back to the original caller - which is AspectJ
			// which will then then log it as a compiler problem.
			throw new AbortCompilation(true, rEx);
		}
	}

	public void afterProcessing(CompilationUnitDeclaration unit, int unitIndex) {
		CompilationAndWeavingContext.leavingPhase(processingToken);
		eWorld.finishedCompilationUnit(unit);
		InterimCompilationResult intRes = new InterimCompilationResult(unit.compilationResult, outputFileNameProvider);
		if (unit.compilationResult.hasErrors()) {
			reportedErrors = true;
		}

		if (intermediateResultsRequestor != null) {
			intermediateResultsRequestor.acceptResult(intRes);
		}

		if (unit.compilationResult.hasErrors() || (isXTerminateAfterCompilation || (reportedErrors && !proceedOnError))) {
			acceptResult(unit.compilationResult);
		} else {
			queueForWeaving(intRes);
		}
	}

	private void queueForWeaving(InterimCompilationResult intRes) {
		resultsPendingWeave.add(intRes);
		if (pipelineStalled) {
			if (resultsPendingWeave.size() >= toWaitFor) {
				pipelineStalled = false;
			}
		}
		if (pipelineStalled) {
			return;
		}
		try {
			if (weaveQueuedEntries()) {
				droppingBackToFullBuild = true;
			}
		} catch (IOException ex) {
			AbortCompilation ac = new AbortCompilation(null, ex);
			throw ac;
		}
	}

	/*
	 * Called from the weaverAdapter once it has finished weaving the class files associated with a given compilation result.
	 */
	public void acceptResult(CompilationResult result) {
		compiler.requestor.acceptResult(result.tagAsAccepted());
		if (compiler.unitsToProcess != null) {
			for (int i = 0; i < compiler.unitsToProcess.length; i++) {
				if (compiler.unitsToProcess[i] != null) {
					if (compiler.unitsToProcess[i].compilationResult == result) {
						compiler.unitsToProcess[i].cleanUp();
						compiler.unitsToProcess[i] = null;
					}
				}
			}
		}
	}

	// helper methods...
	// ==================================================================================

	private List<InterimCompilationResult> getBinarySourcesFrom(Map<String, List<UnwovenClassFile>> binarySourceEntries) {
		// Map is fileName |-> List<UnwovenClassFile>
		List<InterimCompilationResult> ret = new ArrayList<InterimCompilationResult>();
		for (Iterator<String> binIter = binarySourceEntries.keySet().iterator(); binIter.hasNext();) {
			String sourceFileName = binIter.next();
			List<UnwovenClassFile> unwovenClassFiles = binarySourceEntries.get(sourceFileName);
			// XXX - see bugs 57432,58679 - final parameter on next call should be "compiler.options.maxProblemsPerUnit"
			CompilationResult result = new CompilationResult(sourceFileName.toCharArray(), 0, 0, Integer.MAX_VALUE);
			result.noSourceAvailable();
			InterimCompilationResult binarySource = new InterimCompilationResult(result, unwovenClassFiles);
			ret.add(binarySource);
		}
		return ret;
	}

	private void notifyRequestor() {
		for (Iterator iter = resultsPendingWeave.iterator(); iter.hasNext();) {
			InterimCompilationResult iresult = (InterimCompilationResult) iter.next();
			compiler.requestor.acceptResult(iresult.result().tagAsAccepted());
		}
	}

	/** Return true if we've decided to drop back to a full build (too much has changed) */
	private boolean weaveQueuedEntries() throws IOException {
		if (debugPipeline) {
			System.err.println(">.weaveQueuedEntries()");
		}
		for (Iterator iter = resultsPendingWeave.iterator(); iter.hasNext();) {
			InterimCompilationResult iresult = (InterimCompilationResult) iter.next();
			for (int i = 0; i < iresult.unwovenClassFiles().length; i++) {
				weaver.addClassFile(iresult.unwovenClassFiles()[i], false);
			}
		}
		ensureWeaverInitialized(); // by doing this only once, are we saying needToReweaveWorld can't change once the aspects have
		// been stuffed into the weaver?
		if (weaver.needToReweaveWorld() && !isBatchCompile) {
			return true;
		}
		weaver.weave(new WeaverAdapter(this, weaverMessageHandler, progressListener));
		resultsPendingWeave.clear(); // dont need to do those again
		this.eWorld.minicleanup();
		if (debugPipeline) {
			System.err.println("<.weaveQueuedEntries()");
		}
		return false;
	}

	private void ensureWeaverInitialized() {
		if (weaverInitialized) {
			return;
		}
		weaverInitialized = true;
		weaver.setIsBatchWeave(isBatchCompile);
		weaver.prepareForWeave();
		if (weaver.needToReweaveWorld()) {
			if (!isBatchCompile) {
				// force full recompilation from source
				this.incrementalCompilationState.forceBatchBuildNextTimeAround();
				return;
			}
			resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourceSetForFullWeave));
		} else {
			Map binarySourcesToAdd = binarySourceProvider.getBinarySourcesForThisWeave();
			resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourcesToAdd));
		}
	}

	// private void weave() throws IOException {
	// if (debugPipeline)System.err.println("> weave()");
	// // ensure weaver state is set up correctly
	// for (Iterator iter = resultsPendingWeave.iterator(); iter.hasNext();) {
	// InterimCompilationResult iresult = (InterimCompilationResult) iter.next();
	// for (int i = 0; i < iresult.unwovenClassFiles().length; i++) {
	// weaver.addClassFile(iresult.unwovenClassFiles()[i]);
	// }
	// }
	//
	// weaver.setIsBatchWeave(isBatchCompile);
	// weaver.prepareForWeave();
	// if (weaver.needToReweaveWorld()) {
	// if (!isBatchCompile) {
	// //force full recompilation from source
	// this.incrementalCompilationState.forceBatchBuildNextTimeAround();
	// return;
	// }
	// resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourceSetForFullWeave));
	// } else {
	// Map binarySourcesToAdd = binarySourceProvider.getBinarySourcesForThisWeave();
	// resultsPendingWeave.addAll(getBinarySourcesFrom(binarySourcesToAdd));
	// }
	//
	// try {
	// weaver.weave(new WeaverAdapter(this,weaverMessageHandler,progressListener));
	// } finally {
	// weaver.tidyUp();
	// IMessageHandler imh = weaver.getWorld().getMessageHandler();
	// if (imh instanceof WeaverMessageHandler)
	// ((WeaverMessageHandler)imh).resetCompiler(null);
	// }
	// if (debugPipeline)System.err.println("< weave()");
	// }

	private void postWeave() {
		if (debugPipeline) {
			System.err.println("> postWeave()");
		}
		IMessageHandler imh = weaver.getWorld().getMessageHandler();
		if (imh instanceof WeaverMessageHandler) {
			((WeaverMessageHandler) imh).setCurrentResult(null);
		}
		if (!droppingBackToFullBuild) {
			weaver.allWeavingComplete();
		}
		weaver.tidyUp();
		if (imh instanceof WeaverMessageHandler) {
			((WeaverMessageHandler) imh).resetCompiler(null);
		}
		if (debugPipeline) {
			System.err.println("< postWeave()");
		}
	}

	/**
	 * Return true if the compilation unit declaration contains an aspect declaration (either code style or annotation style). It
	 * must inspect the multiple types that may be in a compilation unit declaration and any inner types.
	 */
	private boolean containsAnAspect(CompilationUnitDeclaration cud) {
		TypeDeclaration[] typeDecls = cud.types;
		if (typeDecls != null) {
			for (int i = 0; i < typeDecls.length; i++) { // loop through top level types in the file
				TypeDeclaration declaration = typeDecls[i];
				if (isAspect(declaration)) {
					return true;
				}
				if (declaration.memberTypes != null) {
					TypeDeclaration[] memberTypes = declaration.memberTypes;
					for (int j = 0; j < memberTypes.length; j++) { // loop through inner types
						if (containsAnAspect(memberTypes[j])) {
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	private boolean containsAnAspect(TypeDeclaration tDecl) {
		if (isAspect(tDecl)) {
			return true;
		}
		if (tDecl.memberTypes != null) {
			TypeDeclaration[] memberTypes = tDecl.memberTypes;
			for (int j = 0; j < memberTypes.length; j++) { // loop through inner types
				if (containsAnAspect(memberTypes[j])) {
					return true;
				}
			}
		}
		return false;
	}

	private static final char[] aspectSig = "Lorg/aspectj/lang/annotation/Aspect;".toCharArray();

	private boolean isAspect(TypeDeclaration declaration) {
		// avoid an NPE when something else is wrong in this system ... the real problem will be reported elsewhere
		if (declaration.staticInitializerScope == null) {
			return false;
		}
		if (declaration instanceof AspectDeclaration) {
			return true; // code style
		} else if (declaration.annotations != null) { // check for annotation style
			for (int index = 0; index < declaration.annotations.length; index++) {
				// Cause annotation resolution
				declaration.binding.getAnnotationTagBits();
				Annotation a = declaration.annotations[index];
				if (a.resolvedType == null) {
					continue; // another problem is being reported, so don't crash here
				}
				if (CharOperation.equals(a.resolvedType.signature(), aspectSig)) {
					return true;
				}
			}
		}
		return false;
	}

	// ---
	/**
	 * SECRET: FOR TESTING - this can be used to collect information that tests can verify.
	 */
	public static boolean pipelineTesting = false;
	public static Hashtable<String, String> pipelineOutput = null;

	// Keys into pipelineOutput:
	// compileOrder "[XXX,YYY]" a list of the order in which files will be woven (aspects should be first)
	// filesContainingAspects "NNN" how many input source files have aspects inside
	// 

	public static String getPipelineDebugOutput(String key) {
		if (pipelineOutput == null) {
			return "";
		}
		return pipelineOutput.get(key);
	}

	private final static boolean debugPipeline = false;

	public List<InterimCompilationResult> getResultsPendingWeave() {
		return resultsPendingWeave;
	}

}