This commit was manufactured by cvs2svn to create branch 'APT_32'.

Cherrypick from master 2006-01-09 23:10:35 UTC tyeung <tyeung> 'CR249862 |  Disabling APT processing should clean up gen src dir':
    org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/env/EncodedFileOutputStream.java
    org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/ClasspathUtil.java
    org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/GeneratedSourceFolderManager.java
diff --git a/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/env/EncodedFileOutputStream.java b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/env/EncodedFileOutputStream.java
new file mode 100644
index 0000000..b6826a3
--- /dev/null
+++ b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/env/EncodedFileOutputStream.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2006 BEA Systems, Inc. 
+ * 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:
+ *    jgarms@bea.com - initial API and implementation
+ *    
+ *******************************************************************************/
+package org.eclipse.jdt.apt.core.internal.env;
+
+import java.io.IOException;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Sets the encoding for the IFile on close of the stream
+ */
+public class EncodedFileOutputStream extends BinaryFileOutputStream {
+
+	private final String _charsetName;
+	
+	public EncodedFileOutputStream(IFile file, ProcessorEnvImpl env, String charsetName) {
+		super(file, env);
+		_charsetName = charsetName;
+	}
+	
+	@Override
+	public void close() throws IOException {
+		super.close();
+		if (_charsetName != null) {
+			try {
+				_file.setCharset(_charsetName, null);
+			}
+			catch (CoreException ce) {
+				IOException ioe = new IOException("Could not set charset: " + _charsetName); //$NON-NLS-1$
+				ioe.initCause(ce);
+				throw ioe;
+			}
+		}
+	}
+}
diff --git a/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/ClasspathUtil.java b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/ClasspathUtil.java
new file mode 100644
index 0000000..6f2faff
--- /dev/null
+++ b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/ClasspathUtil.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * 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:
+ *   wharley@bea.com - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.apt.core.internal.generatedfile;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.apt.core.AptPlugin;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+
+/**
+ * Utilities to ensure the generated source folder is (or is not) on the
+ * Java build path as appropriate.
+ */
+public class ClasspathUtil {
+
+	/**
+	 * Given a java project, this function will determine if the specified
+	 * folder is a source folder of the java project. 
+	 * 
+	 * @param jp - the java project
+	 * @param folder - the folder that you want to see if it is a classpath entry for the java project
+	 * @return
+	 * @throws JavaModelException
+	 */
+	public static IClasspathEntry findProjectSourcePath( IJavaProject jp, IFolder folder )
+		throws JavaModelException
+	{
+		IClasspathEntry[] cp = jp.getRawClasspath();
+		IClasspathEntry searchingFor = 
+			JavaCore.newSourceEntry(folder.getFullPath());
+		IPath searchingForPath = searchingFor.getPath();
+		for (int i = 0; i < cp.length; i++) 
+		{
+			if (cp[i].getPath().equals( searchingForPath )) 
+				return cp[i];
+		}
+		return null;
+	}
+
+	/**
+	 * Does the classpath contain the specified path?
+	 * @param jp if non-null, get this project's classpath and ignore cp
+	 * @param cp if non-null, use this classpath and ignore jp
+	 * @param path the entry to look for on the classpath
+	 * @param progressMonitor
+	 * @return
+	 * @throws JavaModelException
+	 */
+	public static boolean isProjectClassPathUpToDate(		
+			IJavaProject jp,
+			IClasspathEntry[] cp,
+			IPath path, 
+			IProgressMonitor progressMonitor)
+		throws JavaModelException
+	{	
+		if( cp == null )
+			cp = jp.getRawClasspath();
+		for (int i = 0; i < cp.length; i++) 
+		{
+			if (cp[i].getPath().equals( path )) 
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	/** 
+	 * removes a classpath entry from the project 
+	 */
+	public static void removeFromProjectClasspath( IJavaProject jp, IFolder folder, IProgressMonitor progressMonitor )
+		throws JavaModelException
+	{			
+		IClasspathEntry[] cp = jp.getRawClasspath();
+		IPath workspaceRelativePath = folder.getFullPath();
+		boolean found = isProjectClassPathUpToDate(jp, cp, workspaceRelativePath, progressMonitor);
+		
+		if( found ){			
+			IPath projectRelativePath = folder.getProjectRelativePath().addTrailingSeparator();
+	
+			// remove entries that are for the specified folder, account for 
+			// multiple entries, and clean up any exclusion entries to the 
+			// folder being removed.
+			int j = 0;
+			for ( int i=0; i<cp.length; i++ )
+			{
+				if (! cp[i].getPath().equals( workspaceRelativePath ) )
+				{
+				
+					// see if we added the generated source dir as an exclusion pattern to some other entry
+					IPath[] oldExclusions = cp[i].getExclusionPatterns();
+					int m = 0;
+					for ( int k = 0; k < oldExclusions.length; k++ )
+					{
+						if ( !oldExclusions[k].equals( projectRelativePath ) )
+						{
+							oldExclusions[m] = oldExclusions[k];
+							m++;
+						}
+					}
+					
+					if ( oldExclusions.length == m )
+					{
+						// no exclusions changed, so we do't need to create a new entry
+						cp[j] = cp[i];
+					}
+					else
+					{
+						// we've removed some exclusion, so create a new entry
+						IPath[] newExclusions = new IPath[ m ];
+						System.arraycopy( oldExclusions, 0, newExclusions, 0, m );
+						cp[j] = JavaCore.newSourceEntry( cp[i].getPath(), cp[i].getInclusionPatterns(), newExclusions, cp[i].getOutputLocation(), cp[i].getExtraAttributes() );
+					}
+					
+					j++;
+				}
+			}
+			
+			// now copy updated classpath entries into new array
+			IClasspathEntry[] newCp = new IClasspathEntry[ j ];
+			System.arraycopy( cp, 0, newCp, 0, j);
+			jp.setRawClasspath( newCp, progressMonitor );
+			
+			if( AptPlugin.DEBUG ){
+				AptPlugin.trace("removed " + workspaceRelativePath + " from classpath"); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+	}
+
+	/**
+	 * returns true if we updated the classpath, false otherwise
+	 */
+	public static boolean updateProjectClasspath( IJavaProject jp, IFolder folder, IProgressMonitor progressMonitor )
+		throws JavaModelException
+	{
+		IClasspathEntry[] cp = jp.getRawClasspath();
+		IPath path = folder.getFullPath();
+		boolean found = ClasspathUtil.isProjectClassPathUpToDate(jp, cp, path, progressMonitor);
+		
+		if (!found) 
+		{
+			// update exclusion patterns
+			ArrayList<IPath> exclusions = new ArrayList<IPath>();
+			for ( int i = 0; i< cp.length; i++ )
+			{
+				if ( cp[i].getPath().isPrefixOf( path ) )
+				{
+					// exclusion patterns must be project-relative paths, and must end with a "/"
+					IPath projectRelativePath = folder.getProjectRelativePath().addTrailingSeparator();
+					
+					// path is contained in an existing source path, so update existing paths's exclusion patterns				
+					IPath[] oldExclusions = cp[i].getExclusionPatterns();
+
+					// don't add if exclusion pattern already contains src dir
+					boolean add = true;
+					for ( int j = 0; j < oldExclusions.length; j++ )
+						if ( oldExclusions[j].equals( projectRelativePath ) )
+							add = false;
+					
+					if ( add )
+					{
+						IPath[] newExclusions;
+						if ( cp[i].getExclusionPatterns() == null )
+							newExclusions = new IPath[1];
+						else
+						{
+							newExclusions = new IPath[ oldExclusions.length + 1 ];
+							System.arraycopy( oldExclusions, 0, newExclusions, 0, oldExclusions.length );
+						}
+						newExclusions[ newExclusions.length - 1 ] = projectRelativePath;
+						cp[i] = JavaCore.newSourceEntry(cp[i].getPath(), cp[i].getInclusionPatterns(), newExclusions, cp[i].getOutputLocation(), cp[i].getExtraAttributes());
+					}
+					
+				}
+				else if ( path.isPrefixOf( cp[i].getPath() ))
+				{
+					// new source path contains an existing source path, so add an exclusion pattern for it
+					exclusions.add( cp[i].getPath().addTrailingSeparator() );
+				}
+			}
+			
+			IPath[] exclusionPatterns = exclusions.toArray( new IPath[exclusions.size()] );
+			IClasspathEntry generatedSourceClasspathEntry = 
+				JavaCore.newSourceEntry(folder.getFullPath(), exclusionPatterns );
+			
+			IClasspathEntry[] newCp = new IClasspathEntry[cp.length + 1];
+			System.arraycopy(cp, 0, newCp, 0, cp.length);
+			newCp[newCp.length - 1] = generatedSourceClasspathEntry;
+			
+			jp.setRawClasspath(newCp, progressMonitor );
+		}
+
+		// return true if we updated the project's classpath entries
+		return !found;
+	}
+	
+	/**
+	 * All methods static.  Clients should not instantiate this class.
+	 */
+	private ClasspathUtil() {
+	}
+
+
+}
diff --git a/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/GeneratedSourceFolderManager.java b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/GeneratedSourceFolderManager.java
new file mode 100644
index 0000000..7d728a1
--- /dev/null
+++ b/org.eclipse.jdt.apt.core/src/org/eclipse/jdt/apt/core/internal/generatedfile/GeneratedSourceFolderManager.java
@@ -0,0 +1,522 @@
+/*******************************************************************************
+ * Copyright (c) 2005 BEA Systems, Inc.
+ * 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:
+ *   wharley@bea.com - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.apt.core.internal.generatedfile;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.jdt.apt.core.AptPlugin;
+import org.eclipse.jdt.apt.core.internal.AptProject;
+import org.eclipse.jdt.apt.core.internal.util.FileSystemUtil;
+import org.eclipse.jdt.apt.core.util.AptConfig;
+import org.eclipse.jdt.apt.core.util.AptPreferenceConstants;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaModelException;
+
+/**
+ * Manage the generated source folder for an APT project.
+ * Every AptProject has a GeneratedSourceFolderManager.  Depending on whether APT
+ * is enabled for the project, there may or may not be an actual generated
+ * source folder on disk; GeneratedSourceFolderManager is responsible for creating
+ * and deleting this folder as needed whenever APT settings are changed.
+ * <p>
+ * The job of the GeneratedSourceFolderManager is to keep the following data
+ * in agreement: 
+ * <ul>
+ * <li>whether APT is enabled</li>
+ * <li>the name of the generated source folder</li>
+ * <li>the existence of the actual folder on disk</li>
+ * <li>the presence of a classpath entry for the folder</li>
+ * <li>problem markers indicating a disagreement in any of the above</li>
+ * </ul>
+ * We attempt to change the classpath entry and the folder on disk whenever
+ * the enabled/disabled state or the folder name change.  These changes are
+ * discovered via the handlePreferenceChange() method. 
+ * <p>
+ * GeneratedSourceFolderManager is responsible only for the folder itself, not
+ * its contents.  Contents are managed by @see GeneratedFileManager.
+ *  
+ */
+public class GeneratedSourceFolderManager {
+	
+	private final AptProject _aptProject;
+
+	/**
+	 * The folder where generated source files are placed.  This will be null until
+	 * the folder is actually created and the project's source path is updated to 
+	 * include the folder.  It will also be null if there was an error creating the
+	 * folder.
+	 */
+	private IFolder _generatedSourceFolder = null;
+	
+	/**
+	 * The name of the generated source folder, relative to the project.  This
+	 * will be identical to the value of the APT_GENSRCDIR preference, except when 
+	 * the preference has changed and this object has not yet been informed.
+	 */
+	private String _generatedSourceFolderName = null;
+	
+	/**
+	 * Should be constructed only by AptProject.  Other clients should call
+	 * @see AptProject#getGeneratedSourceFolderManager() to get this object.
+	 */
+	public GeneratedSourceFolderManager(AptProject aptProject) 
+	{
+		_aptProject = aptProject;
+		final IJavaProject javaProject = aptProject.getJavaProject();
+		
+		// get generated source dir from config 
+		// default value is set in org.eclipse.jdt.apt.core.internal.util.AptCorePreferenceInitializer
+		_generatedSourceFolderName = AptConfig.getString( javaProject, AptPreferenceConstants.APT_GENSRCDIR);
+		// properly initialize the GeneratedFileManager if project path is up-to-date and the generated 
+		// source folder is there.
+		final IFolder folder = getFolder();
+		if(folder.exists()){
+			boolean uptodate = false;
+			try{
+				uptodate = ClasspathUtil.isProjectClassPathUpToDate(javaProject, null, folder.getFullPath(), null);
+			}catch(JavaModelException e){
+				e.printStackTrace();
+			}
+			if( uptodate )
+				_generatedSourceFolder = folder;
+		}	
+	}
+	
+	/**
+	 * Sets the name of the generated soruce folder.  The source folder will not be created 
+	 * and will not be added to the project's source paths.  If there is an existing source
+	 * folder, it will be deleted.
+	 * To properly have the new generated source folder configured, call #ensureGeneratedSourceFolder(). 
+	 * 
+	 * @param newValue The string name of the new generated source folder.  This should be relative 
+	 * to the project root.  Absolute paths are not supported.  The specified string should be 
+	 * a valid folder name for the file system, and should not be an existing source folder for the 
+	 * project.  
+	 * 
+	 * @see #getFolder()
+	 * @see #getFolderName()
+	 */
+	private void configure( String newValue, String oldValue ) 
+	{
+		
+		// bail if they specify null, empty-string or don't change the name of the source folder
+		if ( newValue == null || 
+			 newValue.length() == 0 || 
+			 newValue.equals(oldValue) )
+			return;
+		
+		_aptProject.projectClean( true );
+
+		IFolder srcFolder = null;
+		synchronized ( this )
+		{
+			_generatedSourceFolderName = newValue;
+			srcFolder = _generatedSourceFolder;
+		}
+		
+		// if the preference change occur before we actually
+		// initialized the _generatedSourceFolder. 
+		// This may happen when the pre-processor resource change event occurs after
+		// the preference change event.
+		if( oldValue != null && srcFolder == null ){
+			srcFolder = _aptProject.getJavaProject().getProject().getFolder( oldValue );
+		}
+		
+		resetGeneratedSrcFolder(srcFolder, true);		
+	}
+	
+	/**
+	 * Creates the generated source folder if it doesn't exist. 
+	 * No changes to the classpath will be made.
+	 */
+	public void createGeneratedSourceFolder(){
+		IFolder srcFolder = getFolder();
+		// This most likely means the preference change event hasn't occured yet
+		// and we don't know about the name of the generated source directory.
+		if( srcFolder == null )
+			return;
+		try{
+			srcFolder.refreshLocal( IResource.DEPTH_INFINITE, null );
+			if (!srcFolder.exists()) {
+				if( AptPlugin.DEBUG )
+					AptPlugin.trace("creating " + srcFolder.getProjectRelativePath()); //$NON-NLS-1$
+					
+				FileSystemUtil.makeDerivedParentFolders(srcFolder);
+			}
+		}
+		catch(CoreException ce){
+			AptPlugin.log(ce, "Failure during refreshLocal on " + srcFolder.getProjectRelativePath()); //$NON-NLS-1$
+		}
+		synchronized (this) {
+			_generatedSourceFolder = srcFolder;
+		}
+	}
+	
+	/**
+	 *  Creates the generated source folder if it doesn't exist, and adds it as a source path
+	 *  to the project.  To access the generated source folder, but not have it be created
+	 *  or added as a source path, use getGeneratedSourceFolder().  Note that this method 
+	 *  will take a resource lock if the generated source folder needs to be created on disk, 
+	 *  and it will take a java model lock if the project's source paths need to be updated.
+	 *  Care should be taken when calling this method to ensure that locking behavior is correct.    
+	 *  
+	 *  @return <code>true</code> iff the any resource or classpath has been modified. 
+	 *  return <code>false</code> otherwise.
+	 *  
+	 *  @see #getFolder()
+	 *  @see #isGeneratedSourceFolderConfigured()
+	 */
+	private boolean ensureGeneratedSourceFolder(){
+		
+		boolean reset = false;
+		IFolder curSrcFolder = null;
+	
+		// Determine current state of affairs, with respect to
+		// folder, folder name, and classpath.
+		synchronized( this )
+		{
+			if( _generatedSourceFolder != null ){
+				final IPath srcFolderPath = _generatedSourceFolder.getProjectRelativePath();
+				
+				if( !_generatedSourceFolderName.equals( srcFolderPath.toString()) ){
+					// Folder name has been changed!  Save the current folder so we can clear it out later.
+					reset = true;
+					curSrcFolder = _generatedSourceFolder;
+					_generatedSourceFolder = null;
+				}
+				else {
+					// Folder name and folder are in sync.  Check that folder is on classpath.
+					
+					// If the folder doesn't exist on disk, there is no point examining the classpath.
+					try{
+						_generatedSourceFolder.refreshLocal( IResource.DEPTH_INFINITE, null );
+					}
+					catch(CoreException ce){
+						AptPlugin.log(ce, "Failure during refreshLocal on " + srcFolderPath); //$NON-NLS-1$
+					}
+					if (!_generatedSourceFolder.exists()) {
+						return false;
+					}
+					
+					try {
+						IJavaProject jp = _aptProject.getJavaProject();
+						IClasspathEntry[] cp = jp.getRawClasspath();
+						IPath path = _generatedSourceFolder.getFullPath();
+						if (ClasspathUtil.isProjectClassPathUpToDate(jp, cp, path, null)) {
+							return false;
+						}
+					}
+					catch (JavaModelException jme) {
+						AptPlugin.log(jme, "Failure examining the classpath"); //$NON-NLS-1$
+					}
+				}
+			}
+		}
+		
+		IFolder srcFolder = null;
+		try{
+			if( reset ){
+				// Folder name was out of sync with folder.  Delete the old folder and classpath entry.
+				ClasspathUtil.removeFromProjectClasspath(_aptProject.getJavaProject(), curSrcFolder, null );
+				if ( curSrcFolder.exists() ){
+					if( AptPlugin.DEBUG )
+						AptPlugin.trace("deleting gen src dir " + curSrcFolder.getName() ); //$NON-NLS-1$
+					curSrcFolder.delete( true, false, null );
+				}
+			}
+				
+			// don't take any locks while creating the folder, since we are doing file-system operations
+			srcFolder = getFolder();
+			srcFolder.refreshLocal( IResource.DEPTH_INFINITE, null );
+			if (!srcFolder.exists()) {
+				FileSystemUtil.makeDerivedParentFolders(srcFolder);
+			}
+				
+			//
+			// make sure __generated_src dir is on the cp if not already
+			//
+			ClasspathUtil.updateProjectClasspath( _aptProject.getJavaProject(), srcFolder, null );
+			
+			if(AptPlugin.DEBUG)
+				AptPlugin.trace("Added directory " + srcFolder.getName() + " and updated classpath" ); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		catch(CoreException e){						
+			e.printStackTrace();
+			AptPlugin.log(e, "Failed to create generated source directory"); //$NON-NLS-1$
+			return false;
+		}
+		
+		synchronized ( this )
+		{
+			_generatedSourceFolder = srcFolder;
+			return true;
+		}
+	}
+	
+	
+	/**
+	 *  Invoked when the generated source folder has been deleted.  This will 
+	 *  flush any in-memory state tracking generated files and clean up the project classpath.
+	 *  
+	 *  Note: this should only be called within a resource change event to ensure that the classpath
+	 *  is correct during any build. Resource change event never occurs during a build.
+	 */
+	public void generatedSourceFolderDeleted()
+	{
+		_aptProject.projectClean( false );
+		
+		IFolder srcFolder;
+		synchronized(this){
+			srcFolder = getFolder();
+			_generatedSourceFolder = null;
+		}
+		if(AptPlugin.DEBUG)
+			AptPlugin.trace("nulled out gen src dir " + srcFolder.getName() ); //$NON-NLS-1$
+	}
+
+	/**
+	 * @return get the generated source folder. May return null if
+	 * creation has failed, the folder has been deleted or has not been created.
+	 */
+	public IFolder getFolder(){
+		
+		final String folderName;
+		synchronized (this) {
+			if( _generatedSourceFolder != null )
+				return _generatedSourceFolder;
+			folderName = getFolderName();
+		}		
+		if(folderName == null)
+			return null;
+		
+		return _aptProject.getJavaProject().getProject().getFolder( folderName );
+	}
+	
+	/**
+	 * returns the name of the folder for generated source files.  The name is relative
+	 * to the project root.
+	 * 
+	 * @see #getFolder()
+	 * @see #isGeneratedSourceFolderConfigured()
+	 */
+	public synchronized String getFolderName() 
+	{ 
+		return _generatedSourceFolderName; 
+	}
+	
+	/**
+	 * This method will return the binary output location for the generated source folder.
+	 * If the generated-source folder is not configured (i.e., not created or not added to
+	 * the project's source path, then this method will return the default binary output
+	 * location for the project. 
+	 *
+	 * @return the IPath corresponding to the binary output location for the
+	 * generated source folder. This is relative to the project.
+	 * 
+	 * @throws JavaModelException
+	 * 
+	 * @see #getFolder()
+	 * @see #isGeneratedSourceFolderConfigured()	
+	 */
+	public IPath getBinaryOutputLocation()
+		 throws JavaModelException 
+	{
+		IPath outputRootPath = null;
+		IFolder generatedSourceFolder = getFolder();
+		if ( generatedSourceFolder != null && generatedSourceFolder.exists() )
+		{
+			IClasspathEntry cpe = ClasspathUtil.findProjectSourcePath( _aptProject.getJavaProject(), generatedSourceFolder );
+			if ( cpe != null )
+				outputRootPath = cpe.getOutputLocation();
+		}
+		
+		// no output root, so get project's default output location
+		if ( outputRootPath == null )
+			outputRootPath = _aptProject.getJavaProject().getOutputLocation();
+
+		// output location is relative to the workspace, we want to make it relative to project
+		int segments = outputRootPath.matchingFirstSegments( _aptProject.getJavaProject().getPath() );
+		outputRootPath = outputRootPath.removeFirstSegments( segments );
+		
+		return outputRootPath;
+	}
+
+	/**
+	 * Configure the generated source folder according to whether APT is enabled
+	 * or disabled.  If enabled, the folder will be created and a classpath entry
+	 * will be added.  If disabled, the folder and classpath entry will be removed.
+	 * <p>
+	 * This should only be called on an event thread, with no locks on the project
+	 * or classpath.
+	 * @param enable
+	 */
+	public void setEnabled(boolean enable)
+	{
+		if( AptPlugin.DEBUG ){
+			if( enable )
+				AptPlugin.trace("enabling APT for " + _aptProject.getJavaProject().getElementName()); //$NON-NLS-1$
+			else
+				AptPlugin.trace("disabling APT for " + _aptProject.getJavaProject().getElementName()); //$NON-NLS-1$
+		}
+		if( enable ) {
+			final String folderName = _generatedSourceFolderName;
+			if( AptPlugin.DEBUG ){
+				AptPlugin.trace("configure generated source folder to be " + folderName ); //$NON-NLS-1$
+			}
+			configure(folderName, null);
+		} else{
+			final IFolder srcFolder = getFolder();
+			_aptProject.projectClean(true);
+			resetGeneratedSrcFolder(srcFolder, false);
+		}
+	}
+	
+	/**
+	 * Configure the name of the generated source folder.  If APT is enabled,
+	 * remove the old folder and classpath entry and create new ones.  If
+	 * disabled, simply record the new name.
+	 * <p>
+ 	 * This should only be called on an event thread, with no locks on the project
+	 * or classpath.
+	 * TODO: why does this need to know the old name?  Didn't we get it in the constructor?
+	 * @param oldName can be null if the old value is not known.
+	 * @param newName
+	 */
+	public void changeFolderName(String oldName, String newName)
+	{
+		if (newName == null) {
+			// Null is used to indicate this preference has
+			// been removed, as the project has been deleted.
+			// We do nothing
+			return;
+		}
+		if (newName.equals(oldName)) {
+			// No-op -- same config
+			return;
+		}
+		
+		final boolean aptEnabled = AptConfig.isEnabled(_aptProject.getJavaProject());
+		if( AptPlugin.DEBUG )
+			AptPlugin.trace("configure generated source directory new value = " +  //$NON-NLS-1$
+					newName + 
+					" old value = "  + oldName + //$NON-NLS-1$
+					" APT is enabled = " + aptEnabled); //$NON-NLS-1$
+		if( aptEnabled )
+			// If APT is enabled, 
+			// clean up the old cp entry, delete the old folder, 
+			// create the new one and update the classpath.
+			configure( newName, oldName );
+		else
+			// If APT is not enabled, the folder should not exist
+			// and there should be no entry on the classpath.
+			synchronized (this) {
+				_generatedSourceFolderName = newName;
+			}
+	}
+	
+	/**
+	 * returns true if the specified folder is the source folder used where
+	 * generated files are placed. 
+	 * 
+	 * @param folder - the folder to determine if it is the generated source folder
+	 * @return true if it is the generated source folder, false otherwise.  
+	 * 
+	 * @see #getFolder()
+	 */
+	public boolean isGeneratedSourceFolder( IFolder folder )
+	{
+		return folder != null && folder.equals( getFolder() );
+	}
+	
+	/**
+	 * Cleanup the classpath and schedule a job to delete the generated source folder.
+	 * @param recreate if <code>true</code> configure the generated source directory.
+	 */
+	private void resetGeneratedSrcFolder(final IFolder srcFolder, boolean recreate){
+		// clean up the classpath first so that when we actually delete the 
+		// generated source folder and won't cause a classpath error.
+		if( srcFolder != null ){
+			try{	
+				ClasspathUtil.removeFromProjectClasspath( _aptProject.getJavaProject(), srcFolder, null );		
+			}catch(JavaModelException e){
+				AptPlugin.log( e, "Error occurred deleting old generated src folder " + srcFolder.getName() ); //$NON-NLS-1$
+			}
+		}
+		
+		if( recreate )
+			ensureGeneratedSourceFolder();
+		
+		// delete the generated source folder as well as
+		// all of its derived ancestors that are containers only to the 
+		// generated source folder
+		if( srcFolder != null ){
+			IFolder folderToDelete = srcFolder;		
+			for( IContainer c = srcFolder.getParent(); 
+			 	 c != null && (c instanceof IFolder); 
+			 	 c = c.getParent() ){
+				
+				try{
+					// members can't be empty, there has to be at least 1.
+					// will only delete the parent if it contains only the 
+					// folder that we want to delete.
+					if( c.isDerived() && c.members().length == 1 ){
+						folderToDelete = (IFolder)c;
+					}
+					else
+						break;
+				}catch(CoreException e){
+					AptPlugin.log(e, "failure while accessing member of " + c.getName() ); //$NON-NLS-1$
+					break;
+				}
+			}
+			removeFolder(folderToDelete);
+		}
+	}
+
+	/**
+	 * Remove the specified folder from disk.
+	 * @param srcFolder
+	 */
+	private void removeFolder(final IFolder srcFolder) {
+		if( srcFolder != null ){
+			final IWorkspaceRunnable runnable = new IWorkspaceRunnable(){
+	            public void run(IProgressMonitor monitor)
+	            {		
+	            	if( srcFolder != null ){
+		            	try{
+		            		srcFolder.delete(true, false, null);
+		            	}catch(CoreException e){
+		            		AptPlugin.log(e, "failed to delete old generated source folder " + srcFolder.getName() ); //$NON-NLS-1$
+		            	}catch(OperationCanceledException cancel){
+		            		AptPlugin.log(cancel, "deletion of generated source folder got cancelled"); //$NON-NLS-1$
+		            	}
+	            	}
+	            };
+	        };
+	        IWorkspace ws = _aptProject.getJavaProject().getProject().getWorkspace();
+	        try{
+	        	ws.run(runnable, ws.getRoot(), IWorkspace.AVOID_UPDATE, null);
+	        }catch(CoreException e){
+	    		AptPlugin.log(e, "Runnable for deleting old generated source folder " + srcFolder.getName() + " failed."); //$NON-NLS-1$ //$NON-NLS-2$
+	    	}
+		}
+	}
+	
+}