blob: 4544176b26a73c6e7dab7039eeb094f560f93d05 [file] [log] [blame]
/*******************************************************************************
* 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:
* mkaufman@bea.com - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.apt.core.internal.generatedfile;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourceAttributes;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.dom.AST;
/**
* This class is used for managing generated files.
*
* There are four maps that are used. Two are used to track the relationships
* between parent files & generated files (
* <code>_parentFile2GeneratedFiles</code> & <code>_generatedFile2ParentFiles</code>).
* The other two maps are used to track cached working copies:
* <code>_generatedFile2WorkingCopy</code> maps a generated file to its
* working copy, and <code>_generatedWorkingCopy2OpenParentFiles</code>
* maps a working copy to any parent files that may be open.
*
* The file maps have entries added when a file is generated during a build.
* The file maps & working-copy maps haven entries added added when a file
* is added during a reconcile. There are various entry-points to keep the
* maps up-to-date with respect to life-cycle events on the parent & generated files.
* (e.g., parentFileDeleted(), ).
*
*/
public class GeneratedFileManager {
private final IProject _project;
// Use a weak hash map to allow file managers to get GC'ed if a project
// goes away
private static final Map<IProject, GeneratedFileManager> MANAGERS_MAP =
new WeakHashMap<IProject, GeneratedFileManager>();
/**
* Construction can only take place from within
* the factory method, getGeneratedFileManager().
*/
private GeneratedFileManager(final IProject project) {
_project = project;
}
private static void init()
{
_initialized = true;
IWorkspace workspace = ResourcesPlugin.getWorkspace();
// register resource-changed listener
int mask = IResourceChangeEvent.PRE_BUILD | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE;
workspace.addResourceChangeListener( new ResourceChangedListener(), mask );
// register element-changed listener
mask = ElementChangedEvent.POST_CHANGE;
JavaCore.addElementChangedListener( new ElementChangedListener(), mask );
}
public static synchronized List<GeneratedFileManager> getGeneratedFileManagers() {
return new ArrayList(MANAGERS_MAP.values());
}
public static synchronized GeneratedFileManager getGeneratedFileManager(final IProject project)
{
if ( ! _initialized )
init();
GeneratedFileManager gfm = MANAGERS_MAP.get(project);
if (gfm != null)
return gfm;
gfm = new GeneratedFileManager(project);
MANAGERS_MAP.put(project, gfm);
return gfm;
}
/**
* Return the file and a flag indicating if the content was modified.
*
* @param parentFile
* @param typeName
* @param contents
* @param progressMonitor
* @param charsetName
* @return - the newly created IFile along with whether it was modified
* @throws CoreException
* @throws UnsupportedEncodingException
*/
public synchronized FileGenerationResult generateFileDuringBuild(
IFile parentFile,
IJavaProject javaProject,
String typeName,
String contents,
IProgressMonitor progressMonitor,
String charsetName )
throws CoreException, UnsupportedEncodingException
{
try
{
IProject project = javaProject.getProject();
IFolder folder = ensureGeneratedSourceFolder( javaProject, progressMonitor );
IFile file = getIFileForTypeName( typeName, javaProject, progressMonitor );
byte[] bytes;
if ( charsetName == null || charsetName == "" )
bytes = contents.getBytes();
else
bytes = contents.getBytes( charsetName );
InputStream is = new ByteArrayInputStream( bytes );
boolean contentsDiffer = true;
if ( !file.exists() )
{
file.create( is, true, progressMonitor );
}
else
{
// Check if the content has changed
InputStream oldData = null;
try {
oldData = new BufferedInputStream(file.getContents());
contentsDiffer = !compareStreams(oldData, is);
}
catch (CoreException ce) {
// Do nothing. Assume the new content is different
}
finally {
is.reset();
if (oldData != null) {
try {
oldData.close();
}
catch (IOException ioe)
{}
}
}
if (contentsDiffer) {
makeReadOnly( file, false );
file.setContents( is, true, true, progressMonitor );
}
}
file.setDerived( true );
makeReadOnly( file, true );
addEntryToFileMaps( parentFile, file );
return new FileGenerationResult(file, contentsDiffer);
}
catch ( Throwable t )
{
t.printStackTrace();
}
return null;
}
/**
* Return true if the content of the streams is identical,
* false if not.
*/
private static boolean compareStreams(InputStream is1, InputStream is2) {
try {
int b1 = is1.read();
while(b1 != -1) {
int b2 = is2.read();
if(b1 != b2) {
return false;
}
b1 = is1.read();
}
int b2 = is2.read();
if(-1 != b2) {
return false;
}
return true;
}
catch (IOException ioe) {
return false;
}
}
/**
* TODO: figure out how to create a working copy with a client-specified character set
*
*
* @param parentCompilationUnit
* @param typeName
* @param contents
* @param workingCopyOwner
* @param problemRequestor
* @param progressMonitor
* @return
*/
public synchronized FileGenerationResult generateFileDuringReconcile(
ICompilationUnit parentCompilationUnit, String typeName,
String contents, WorkingCopyOwner workingCopyOwner,
IProblemRequestor problemRequestor, IProgressMonitor progressMonitor )
{
ICompilationUnit workingCopy = null;
FileGenerationResult result = null;
try
{
//
// get working copy (either from cache or create a new one)
//
workingCopy = getCachedWorkingCopy( parentCompilationUnit, typeName );
if ( workingCopy == null )
{
// create a new working copy
workingCopy = createNewWorkingCopy(
parentCompilationUnit, typeName, contents,
workingCopyOwner, problemRequestor, progressMonitor);
workingCopy.reconcile(AST.JLS3, true, workingCopyOwner,
progressMonitor);
result = new FileGenerationResult((IFile)workingCopy.getResource(), true);
}
else
{
//
// Update working copy's buffer with the contents of the type
//
boolean modified = updateWorkingCopy( contents, workingCopy, workingCopyOwner, progressMonitor );
result = new FileGenerationResult((IFile)workingCopy.getResource(), modified);
}
return result;
}
catch (JavaModelException jme)
{
jme.printStackTrace();
}
catch (CoreException ce)
{
ce.printStackTrace();
}
return new FileGenerationResult((IFile)workingCopy.getResource(), true);
}
public synchronized boolean isGeneratedFile( IFile f )
{
Set<IFile> s = _generatedFile2ParentFiles.get( f );
if ( s == null || s.isEmpty() )
return false;
else
return true;
}
public synchronized boolean isParentFile( IFile f )
{
Set<IFile> s = _parentFile2GeneratedFiles.get( f );
if ( s == null || s.isEmpty() )
return false;
else
return true;
}
public synchronized boolean isGeneratedSourceFolder( IFolder folder )
throws CoreException
{
// use getGeneratedSourceFolder() here. Bad things can happen if we try to
// create the generated source folder when this is invoked from a resource
// change listener
if ( folder != null && folder.equals( getGeneratedSourceFolder() ) )
return true;
else
return false;
}
/**
*
* @param parent - the parent file that you want to get generated files for
* @return Set of IFile instances that are the files known to be generated
* by this parent
*/
public synchronized Set<IFile> getGeneratedFilesForParent( IFile parent )
{
Set<IFile> s = _parentFile2GeneratedFiles.get( parent );
if (s == null )
s = Collections.emptySet();
return s;
}
/**
* Invoked whenever we need to discard a generated working copy
*/
public synchronized void discardGeneratedWorkingCopy( IFile derivedFile, IFile parentFile )
throws JavaModelException
{
removeFromWorkingCopyMaps( derivedFile, parentFile );
}
/**
* Invoked whenever a parent working copy has been discarded
*/
public synchronized void parentWorkingCopyDiscarded( IFile parentFile )
throws JavaModelException
{
Set<IFile> derivedFiles = _parentFile2GeneratedFiles.get( parentFile );
if ( derivedFiles == null || derivedFiles.size() == 0 )
return;
Iterator<IFile> it = derivedFiles.iterator();
while ( it.hasNext() )
{
IFile f = it.next();
discardGeneratedWorkingCopy( f, parentFile );
}
}
/**
* Invoked whenever a parent file has been deleted
*/
public synchronized void parentFileDeleted( IFile parent, IProgressMonitor monitor )
throws CoreException
{
Set<IFile> derivedFiles = _parentFile2GeneratedFiles.get( parent );
Iterator<IFile> it = derivedFiles.iterator();
while ( it.hasNext() )
{
IFile generatedFile = it.next();
deleteGeneratedFile( generatedFile, parent, monitor );
}
}
/**
* Invoked whenever we need to delete a generated file (e.g., the parent file has been deleted,
* or a parent stops generating a specific child)
*/
public synchronized boolean deleteGeneratedFile(IFile generatedFile, IFile parentFile, IProgressMonitor progressMonitor )
throws CoreException
{
removeFromFileMaps( generatedFile, parentFile );
Set<IFile> parents = _generatedFile2ParentFiles.get( generatedFile );
// this can be empty, but it shouldn't be null here unless parentFile was never a parent of generatedFile
if ( parents == null ) throw new RuntimeException("unexpected null value for parents set for file " + generatedFile);
boolean deleted = false;
if (parents == null || parents.size() == 0) {
generatedFile.delete(true, true, progressMonitor);
deleted = true;
}
return deleted;
}
public synchronized void generatedFileDeleted( IFile generatedFile, IProgressMonitor progressMonitor )
throws JavaModelException, CoreException
{
Set<IFile> parents = _generatedFile2ParentFiles.get( generatedFile );
if ( parents == null || parents.isEmpty() )
return;
Iterator<IFile> it = parents.iterator();
while ( it.hasNext() )
{
IFile parentFile = it.next();
removeFromWorkingCopyMaps( generatedFile, parentFile );
removeFromFileMaps( generatedFile, parentFile );
}
}
/**
* given file f, return the typename corresponding to the file. This assumes
* that derived files use java naming rules (i.e., type "a.b.c" will be file
* "a/b/c.java".
*/
private String getTypeNameForDerivedFile( IFile f )
{
IPath p = f.getFullPath();
IProject project = f.getProject();
IFolder folder = project.getFolder( GENERATED_SOURCE_FOLDER_NAME );
IPath generatedSourcePath = folder.getFullPath();
int count = p.matchingFirstSegments( generatedSourcePath );
p = p.removeFirstSegments( count );
String s = p.toPortableString();
int idx = s.lastIndexOf( '.' );
s = p.toPortableString().replace( '/', '.' );
return s.substring( 0, idx );
}
/**
* Given a typename a.b.c, this will return the IFile for the
* type name, where the IFile is in the GENERATED_SOURCE_FOLDER_NAME.
* @param typeName
* @return
*/
private IFile getIFileForTypeName( String typeName, IJavaProject javaProject, IProgressMonitor progressMonitor )
throws CoreException
{
// split the type name into its parts
String[] parts = typeName.split( "\\.");
IFolder folder = ensureGeneratedSourceFolder( javaProject, progressMonitor );
// create folders for the package parts
int i = 0;
for ( ;i < parts.length - 1; i++ )
{
folder = folder.getFolder( parts[i] );
if ( !folder.exists() )
folder.create( true, false, null );
}
String fileName = parts[i] + ".java";
IFile file = folder.getFile( fileName );
return file;
}
/**
* 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()
*
* @see #getGeneratedSourceFolder()
*/
private IFolder ensureGeneratedSourceFolder( IJavaProject javaProject, IProgressMonitor progressMonitor )
throws CoreException
{
if ( _generatedSourceFolder == null)
{
_generatedSourceFolder = getGeneratedSourceFolder();
_generatedSourceFolder.refreshLocal( IResource.DEPTH_INFINITE, progressMonitor );
if (!_generatedSourceFolder.exists())
_generatedSourceFolder.create(true, false, progressMonitor );
//
// make sure __generated_src dir is on the cp if not already
//
updateProjectClasspath( javaProject, _generatedSourceFolder, progressMonitor );
}
return _generatedSourceFolder;
}
/**
* Will return an IFolder corresponding to the generated source folder name. The result
* IFolder may not exist and may not necessarily be on the java project's classpath.
* To ensure that the generated source folder is created and added to as source path
* to the project, call ensureGeneratedSourceFolder().
*
* @see #ensureGeneratedSourceFolder(IJavaProject, IProgressMonitor)
*/
public synchronized IFolder getGeneratedSourceFolder()
{
if ( _generatedSourceFolder == null)
return _project.getFolder( GENERATED_SOURCE_FOLDER_NAME );
else
return _generatedSourceFolder;
}
// TODO - change this to return an IFolder
public synchronized java.io.File getGeneratedOutputFile( IJavaProject jp )
throws JavaModelException, CoreException
{
IPath outputRoot = null;
IFolder f = getGeneratedSourceFolder();
if ( f != null && f.exists() )
{
IClasspathEntry cpe = findProjectSourcePath( jp, f, null );
if ( cpe != null )
outputRoot = cpe.getOutputLocation();
}
if ( outputRoot == null )
outputRoot = jp.getOutputLocation();
// output location is relative to the workspace, we want to make it relative to project
int segments = outputRoot.matchingFirstSegments( jp.getPath() );
outputRoot = outputRoot.removeFirstSegments( segments );
// TODO - use getRawLocation() or getLocation()? sometimes getRawLocation() returns null. Investigate
IPath projectRoot = jp.getProject().getRawLocation();
if ( projectRoot == null )
projectRoot = jp.getProject().getLocation();
java.io.File file = projectRoot.toFile();
file = new java.io.File( file, outputRoot.toFile().getPath() );
return file;
}
//
// check cache to see if we already have a working copy
//
private ICompilationUnit getCachedWorkingCopy( ICompilationUnit parentCompilationUnit, String typeName )
throws CoreException
{
IFile derivedFile = getIFileForTypeName( typeName, parentCompilationUnit.getJavaProject(), null /*progressMonitor*/ );
ICompilationUnit workingCopy = (ICompilationUnit) _generatedFile2WorkingCopy.get( derivedFile );
if ( workingCopy != null )
addEntryToWorkingCopyMaps( parentCompilationUnit, workingCopy );
return workingCopy;
}
private ICompilationUnit createNewWorkingCopy(ICompilationUnit parentCompilationUnit, String typeName,
String contents, WorkingCopyOwner workingCopyOwner,
IProblemRequestor problemRequestor, IProgressMonitor progressMonitor)
throws CoreException, JavaModelException
{
IProject project = parentCompilationUnit.getResource().getProject();
IJavaProject jp = parentCompilationUnit.getJavaProject();
//
// create folder for generated source files
//
IFolder folder = ensureGeneratedSourceFolder(jp, progressMonitor );
//
// figure out package part of type & file name
//
String pkgName;
String fname;
int idx = typeName.lastIndexOf( '.' );
if ( idx > 0 )
{
pkgName = typeName.substring( 0, idx );
fname =
typeName.substring(idx + 1, typeName.length()) + ".java";
}
else
{
pkgName = "";
fname = typeName + ".java";
}
//
// create compilation unit
//
IPackageFragmentRoot root = jp.getPackageFragmentRoot(folder);
IPackageFragment pkgFragment =
root.createPackageFragment( pkgName, true, null );
ICompilationUnit cu = pkgFragment.getCompilationUnit( fname );
if ( cu == null || ! cu.getResource().exists() )
{
cu = pkgFragment.createCompilationUnit(
fname, contents, true, progressMonitor );
}
else
{
makeReadOnly( cu, false );
}
//
// TODO: can we call getWorkingCopy here?
//
cu.becomeWorkingCopy(problemRequestor, progressMonitor);
ICompilationUnit workingCopy = cu;
//
// update maps
//
addEntryToWorkingCopyMaps( parentCompilationUnit, workingCopy );
// we save this here since the resource has to exist on disk
workingCopy.commitWorkingCopy( true, progressMonitor );
//
// make the file derived so that it is not checked into source control.
//
makeDerived( workingCopy );
//
// make working copy read-only
//
makeReadOnly( workingCopy, true );
return workingCopy;
}
private void makeReadOnly( ICompilationUnit cu, boolean readOnly )
throws CoreException
{
IResource r = cu.getResource();
makeReadOnly( r, readOnly );
}
/**
* make the compilation unit read-only
*/
private void makeReadOnly( IResource r, boolean readOnly )
throws CoreException
{
if ( r.exists() )
{
ResourceAttributes ra = r.getResourceAttributes();
if (ra == null)
ra = new ResourceAttributes();
ra.setReadOnly( readOnly );
r.setResourceAttributes(ra);
}
}
private void makeDerived( ICompilationUnit cu )
throws CoreException
{
IResource r = cu.getResource();
if ( r.exists() )
r.setDerived( true );
}
/**
* Returns true if the file was modified
*/
private static boolean updateWorkingCopy(
String contents, ICompilationUnit workingCopy,
WorkingCopyOwner workingCopyOwner, IProgressMonitor progressMonitor )
throws JavaModelException
{
IBuffer b = workingCopy.getBuffer();
char[] oldBuf = b.getCharacters();
// Diff the contents, and only set if they differ
if (oldBuf.length == contents.length()) {
boolean contentsMatch = true;
for (int i=0; i<oldBuf.length; i++) {
if (oldBuf[i] != contents.charAt(i)) {
contentsMatch = false;
break;
}
}
if (contentsMatch) {
// No change, no need to update buffer
return false;
}
}
b.setContents(contents);
workingCopy.reconcile(AST.JLS3, true, workingCopyOwner,
progressMonitor);
return true;
}
private void addEntryToWorkingCopyMaps( ICompilationUnit parentCompilationUnit, ICompilationUnit workingCopy )
{
IFile parentFile = (IFile) parentCompilationUnit.getResource();
IFile generatedFile = (IFile) workingCopy.getResource();
addEntryToFileMaps( parentFile, generatedFile );
ICompilationUnit cu = (ICompilationUnit)_generatedFile2WorkingCopy.get( generatedFile );
Set<IFile> parents = _generatedWorkingCopy2OpenParentFiles.get( workingCopy);
if ( cu != null )
{
//assert( cu.equals( workingCopy ) ) : "unexpected different instances of working copy for the same type";
if ( !cu.equals(workingCopy) ) throw new RuntimeException( "unexpected different instances of working copy for the same type" );
if ( parents == null || parents.size() < 1 ) throw new RuntimeException( "Unexpected size of open-parents set. Expected size >= 0");
}
else
{
_generatedFile2WorkingCopy.put( generatedFile, workingCopy );
}
if ( parents == null )
{
parents = new HashSet<IFile>();
_generatedWorkingCopy2OpenParentFiles.put( workingCopy, parents );
}
parents.add( parentFile );
}
private void addEntryToFileMaps( IFile parentFile, IFile generatedFile )
{
// add parent file -> set of derived files
Set<IFile> fileSet = _parentFile2GeneratedFiles.get( parentFile );
if ( fileSet == null )
{
fileSet = new HashSet();
_parentFile2GeneratedFiles.put( parentFile, fileSet );
}
fileSet.add( generatedFile );
// add derived file -> set of parent files
fileSet = _generatedFile2ParentFiles.get( generatedFile );
if ( fileSet == null )
{
fileSet = new HashSet();
_generatedFile2ParentFiles.put( generatedFile, fileSet );
}
fileSet.add( parentFile );
}
private void removeFromFileMaps( IFile generatedFile, IFile parentFile )
throws CoreException
{
if (_generatedFile2WorkingCopy.containsKey(generatedFile))
discardGeneratedWorkingCopy(generatedFile, parentFile);
Set<IFile> derivedFiles = _parentFile2GeneratedFiles.get(parentFile);
// assertions
if (derivedFiles == null)
throw new RuntimeException(
"derivedFiles is null and it shouldn't be");
if (!derivedFiles.contains(generatedFile))
throw new RuntimeException(
"derivedFiles does not contain fileToDelete");
derivedFiles.remove(generatedFile);
// update _derivedFile2Parents map
Set<IFile> parents = _generatedFile2ParentFiles.get(generatedFile);
// assertions
if (parents == null)
throw new RuntimeException(" parents is null and it shouldn't be");
if (!parents.contains(parentFile))
throw new RuntimeException("parents set does not contain parent");
parents.remove(parentFile);
}
private void removeFromWorkingCopyMaps( IFile derivedFile, IFile parentFile )
throws JavaModelException
{
ICompilationUnit workingCopy = _generatedFile2WorkingCopy.get( derivedFile );
if ( workingCopy == null )
return;
Set<IFile> parents = _generatedWorkingCopy2OpenParentFiles.get( workingCopy );
// TODO: change these to assertions
if ( parents == null ) throw new RuntimeException( "parents == null and it shouldnt");
if ( ! parents.contains( parentFile )) throw new RuntimeException("parents set should contain parentCompilationUnit");
// remove entry from parents _derivedWorkingCopy2OpenParentFiles
parents.remove( parentFile );
// and remove entry from _derivedFile2WorkingCopy
if ( parents.size() == 0 )
{
_generatedFile2WorkingCopy.remove( derivedFile );
workingCopy.discardWorkingCopy();
}
}
private void clearWorkingCopyMaps()
{
// first discard all working copies
for ( ICompilationUnit workingCopy : _generatedFile2WorkingCopy.values() )
{
try
{
workingCopy.discardWorkingCopy();
}
catch( JavaModelException jme )
{
// TODO: deal with this
jme.printStackTrace();
}
}
_generatedWorkingCopy2OpenParentFiles.clear();
_generatedFile2WorkingCopy.clear();
}
private void clearAllMaps()
{
clearWorkingCopyMaps();
// now clear file maps
_parentFile2GeneratedFiles.clear();
_generatedFile2ParentFiles.clear();
}
private IClasspathEntry findProjectSourcePath( IJavaProject jp, IFolder folder, IProgressMonitor progressMonitor )
throws JavaModelException
{
IClasspathEntry[] cp = jp.getRawClasspath();
IClasspathEntry searchingFor =
JavaCore.newSourceEntry(folder.getFullPath());
IPath searchingForPath = searchingFor.getPath();
boolean found = false;
for (int i = 0; i < cp.length; i++)
{
if (cp[i].getPath().equals( searchingForPath ))
{
return cp[i];
}
}
return null;
}
private void updateProjectClasspath( IJavaProject jp, IFolder folder, IProgressMonitor progressMonitor )
throws JavaModelException
{
IClasspathEntry[] cp = jp.getRawClasspath();
IClasspathEntry generatedSourceClasspathEntry =
JavaCore.newSourceEntry(folder.getFullPath());
boolean found = false;
for (int i = 0; i < cp.length; i++)
{
if (cp[i].getPath().equals(generatedSourceClasspathEntry.getPath()))
{
found = true;
break;
}
}
if (!found)
{
IClasspathEntry[] newCp = new IClasspathEntry[cp.length + 1];
System.arraycopy(cp, 0, newCp, 0, cp.length);
newCp[newCp.length - 1] = generatedSourceClasspathEntry;
jp.setRawClasspath(newCp, progressMonitor );
}
}
private void removeFromProjectClasspath( IJavaProject jp, IFolder folder, IProgressMonitor progressMonitor )
throws JavaModelException
{
IClasspathEntry[] cp = jp.getRawClasspath();
IClasspathEntry folderClasspathEntry =
JavaCore.newSourceEntry(folder.getFullPath());
// remove entries that are for the specified folder. Account for
// multiple entries.
int j = 0;
for ( int i=0; i<cp.length; i++ )
{
if (! cp[i].getPath().equals(folderClasspathEntry.getPath()) )
{
cp[j] = cp[i];
j++;
}
}
// now copy into new array
IClasspathEntry[] newCp = new IClasspathEntry[ j ];
System.arraycopy( cp, 0, newCp, 0, j);
jp.setRawClasspath( newCp, progressMonitor );
}
public synchronized void projectClosed()
{
clearWorkingCopyMaps();
}
public synchronized void projectClean( boolean deleteFiles )
{
clearAllMaps();
// delete the generated source dir
if ( deleteFiles )
{
IFolder f = _project.getFolder( GENERATED_SOURCE_FOLDER_NAME );
if ( f != null && f.exists() )
{
// delete the contents of the generated source folder, but don't delete
// the generated source folder because that will cause a classpath change,
// which will force the next build to be a full build.
try
{
IResource[] members = f.members();
for ( int i = 0; i<members.length; i++ )
members[i].delete( true, null );
}
catch ( CoreException ce )
{
ce.printStackTrace();
}
}
}
}
/**
* Inovked when a project has been deleted
*/
public synchronized void projectDeleted()
{
//
// remove this project from the managers map. Some other clients may still
// have a reference to this, but that should be fine since the project is being
// deleted. We'll just empty out member fields rather than
// setting them to null to avoid NPEs.
//
synchronized( this.getClass() )
{
MANAGERS_MAP.remove( _project );
}
// TODO: eventually make this true. Right now, the resource tree is locked
// when we get the project-deleted event, so we can't delete any files.
projectClean( false );
}
/**
* Invoked when the generated source folder has been deleted.
*/
public synchronized void generatedSourceFolderDeleted()
throws CoreException
{
// jdt-core will remove the generated source folder from the java
// project's classpath, so we'll just clean out our maps.
projectClean( false );
_generatedSourceFolder = null;
}
/**
* This method should only be used for testing purposes to ensure
* that maps contain entries when we expect them to.
*/
public synchronized boolean containsWorkingCopyMapEntriesForParent( IFile f )
{
Collection<Set<IFile>> parentSets = _generatedWorkingCopy2OpenParentFiles.values();
if ( parentSets != null )
{
for( Set<IFile> s : parentSets )
{
if ( s.contains( f ) )
return true;
}
}
Set<IFile> generatedFiles = _parentFile2GeneratedFiles.get( f );
if ( generatedFiles != null )
{
for ( IFile gf : generatedFiles )
{
ICompilationUnit cu = _generatedFile2WorkingCopy.get( gf );
if ( cu != null )
{
Set<IFile> parents = _generatedWorkingCopy2OpenParentFiles.get( cu );
if ( parents.contains( cu ) || parents.size() == 0 )
return true;
}
}
}
return false;
}
/**
* map from IFile of parent file to Set <IFile>of generated files
*/
private Map<IFile, Set<IFile>> _parentFile2GeneratedFiles = new HashMap();
/**
* map from IFile of generated file to Set <IFile>of parent files
*/
private Map<IFile, Set<IFile>> _generatedFile2ParentFiles = new HashMap();
/**
* Map from a the working copy of a generated file to its *open* parents. Note that
* the set of parent files are only those parent files that have an open editor.
* This set should be a subset for a correpsonding entry in the _generatedFile2Parents map.
*/
private Map<ICompilationUnit, Set<IFile>> _generatedWorkingCopy2OpenParentFiles = new HashMap();
/**
* Map from type name to the working copy in memory of that type name
*
* Map<String, ICompilationUnit>
*/
private Map<IFile, ICompilationUnit> _generatedFile2WorkingCopy = new HashMap();
/**
* The folder where generated source files are placed
*/
private IFolder _generatedSourceFolder;
private static boolean _initialized = false;
private static final String GENERATED_SOURCE_FOLDER_NAME = "__generated_src";
}