blob: fdbb9267be495505e88a13d771bbe8ddf8e8e047 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005, 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:
* Konstantin Komissarchik - initial API and implementation
******************************************************************************/
package org.eclipse.jst.common.jdt.internal.classpath;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.common.frameworks.CommonFrameworksPlugin;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.ModuleCoreNature;
import org.eclipse.wst.common.componentcore.internal.resources.VirtualArchiveComponent;
import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;
import org.eclipse.wst.common.componentcore.resources.IVirtualReference;
import org.eclipse.wst.common.componentcore.resources.IVirtualResource;
/**
* @author <a href="mailto:kosta@bea.com">Konstantin Komissarchik</a>
*/
public abstract class FlexibleProjectContainer
implements IClasspathContainer
{
protected static final class PathType
{
public static final PathType
LIB_DIRECTORY = new PathType(),
CLASSES_DIRECTORY = new PathType();
}
private static ClasspathDecorationsManager decorations;
static
{
// Register the resource listener that will listen for changes to
// resources relevant to flexible project containers across the
// workspace and refresh them as necessary.
Listener.register();
// Read the decorations from the workspace metadata.
final String plugin = CommonFrameworksPlugin.PLUGIN_ID;
decorations = new ClasspathDecorationsManager( plugin );
}
private static final String SEPARATOR = "!"; //$NON-NLS-1$
public static String getDecorationManagerKey(IProject project, String container){
return project.getName() + SEPARATOR + container;
}
protected final IPath path;
protected final IJavaProject owner;
protected final IProject project;
private final IPath[] paths;
private final PathType[] pathTypes;
private final List entries;
private final IClasspathEntry[] cpentries;
private static final Set containerTypes = new HashSet();
public FlexibleProjectContainer( final IPath path,
final IJavaProject owner,
final IProject project,
final IPath[] paths,
final PathType[] types )
{
this.path = path;
this.owner = owner;
this.project = project;
this.paths = paths;
this.pathTypes = types;
if( ! isFlexibleProject( this.project ) )
{
// Silently noop if the referenced project is not a flexible
// project. Should I be doing something else here?
this.entries = Collections.EMPTY_LIST;
this.cpentries = new IClasspathEntry[ 0 ];
return;
}
addFlexibleProjectContainerType( path.segment( 0 ) );
this.entries = computeClasspathEntries();
this.cpentries = new IClasspathEntry[ this.entries.size() ];
for( int i = 0, n = this.entries.size(); i < n; i++ )
{
IPath entryPath = (IPath) this.entries.get( i );
IResource resource =ResourcesPlugin.getWorkspace().getRoot().findMember(entryPath);
if(null != resource && resource.getType() == IResource.PROJECT)
this.cpentries[ i ] = JavaCore.newProjectEntry(entryPath, false);
else
this.cpentries[ i ] = newLibraryEntry( entryPath );
}
}
public int getKind()
{
return K_APPLICATION;
}
public IPath getPath()
{
return this.path;
}
public IClasspathEntry[] getClasspathEntries()
{
return this.cpentries;
}
public boolean isOutOfDate( final IResourceDelta delta )
{
if( delta == null )
{
return false;
}
final List currentEntries = computeClasspathEntries();
return ! this.entries.equals( currentEntries );
}
public abstract void refresh();
static ClasspathDecorationsManager getDecorationsManager()
{
return decorations;
}
private List computeClasspathEntries()
{
final List entries = new ArrayList();
final IVirtualComponent vc
= ComponentCore.createComponent( this.project );
if( vc == null )
{
return entries;
}
final Set existingEntries
= ClasspathUtil.getResolvedClasspath( this.owner, getPath() );
IVirtualReference[] refs = vc.getReferences();
IVirtualComponent comp = null;
Set jarsHandled = new HashSet();
String jarName = null;
for (int i = 0; i < refs.length; i++) {
comp = refs[i].getReferencedComponent();
if (!refs[i].getRuntimePath().equals(paths[0].makeAbsolute()))
continue;
jarName = refs[i].getArchiveName();
if(null != jarName){
jarsHandled.add(jarName);
}
IPath newPath = null;
if (comp.isBinary()) {
VirtualArchiveComponent archiveComp = (VirtualArchiveComponent) comp;
if (archiveComp.getArchiveType().equals(VirtualArchiveComponent.CLASSPATHARCHIVETYPE)) {
// do not process components dynamically computed from the Java classpath
continue;
}
java.io.File diskFile = archiveComp.getUnderlyingDiskFile();
if (diskFile.exists()) {
newPath =new Path(diskFile.getAbsolutePath());
} else {
IFile iFile = archiveComp.getUnderlyingWorkbenchFile();
if (!entries.contains(iFile.getFullPath())){
newPath = iFile.getFullPath();
}
}
} else {
IProject project = comp.getProject();
newPath = project.getFullPath();
}
if( newPath != null && ! existingEntries.contains( newPath ) )
{
entries.add( newPath );
}
}
for( int i = 0; i < this.paths.length; i++ )
{
final IVirtualFolder rootFolder = vc.getRootFolder();
final IVirtualFolder vf = rootFolder.getFolder( paths[ i ] );
if( this.pathTypes[ i ] == PathType.LIB_DIRECTORY )
{
final IVirtualResource[] contents;
try
{
contents = members( vf );
}
catch( CoreException e )
{
CommonFrameworksPlugin.log( e );
continue;
}
for( int j = 0; j < contents.length; j++ )
{
final IResource r = contents[ j ].getUnderlyingResource();
final IPath p = r.getFullPath();
if(!jarsHandled.contains(p.lastSegment()) && isJarFile( r ) )
{
jarsHandled.add(p.lastSegment());
if( ! existingEntries.contains( p ) )
{
entries.add( p );
}
}
}
}
else
{
final IContainer[] uf = vf.getUnderlyingFolders();
for( int j = 0; j < uf.length; j++ )
{
final IPath p = uf[ j ].getFullPath();
if( ! jarsHandled.contains( p.lastSegment() ) &&
! isSourceOrOutputDirectory( p ) )
{
jarsHandled.add(p.lastSegment());
if( ! existingEntries.contains( p ) )
{
entries.add( p );
}
}
}
}
}
return entries;
}
// TODO: This method was created to provide a safe last-minute workaround
// for the issue described in https://bugs.eclipse.org/bugs/show_bug.cgi?id=162974.
// This code needs to be revisited in a future release to find a more
// permanent solution.
protected IVirtualResource[] members( final IVirtualFolder vf )
throws CoreException
{
return vf.members();
}
private IClasspathEntry newLibraryEntry( final IPath p )
{
IPath srcpath = null;
IPath srcrootpath = null;
IClasspathAttribute[] attrs = {};
IAccessRule[] access = {};
final ClasspathDecorations dec
= decorations.getDecorations( getDecorationManagerKey(project, getPath().toString()), p.toString() );
if( dec != null )
{
srcpath = dec.getSourceAttachmentPath();
srcrootpath = dec.getSourceAttachmentRootPath();
attrs = dec.getExtraAttributes();
}
return JavaCore.newLibraryEntry( p, srcpath, srcrootpath, access, attrs,
false );
}
private boolean isSourceOrOutputDirectory( final IPath aPath )
{
if( isJavaProject( this.project ) )
{
try
{
final IJavaProject jproject = JavaCore.create( this.project );
final IClasspathEntry[] cp = jproject.getRawClasspath();
for( int i = 0; i < cp.length; i++ )
{
final IClasspathEntry cpe = cp[ i ];
if( cpe.getEntryKind() == IClasspathEntry.CPE_SOURCE )
{
final IPath src = cpe.getPath();
final IPath output = cpe.getOutputLocation();
if( src.equals( aPath ) ||
output != null && output.equals( aPath ) )
{
return true;
}
}
}
if( jproject.getOutputLocation().equals( aPath ) )
{
return true;
}
}
catch( JavaModelException e )
{
CommonFrameworksPlugin.log( e );
}
}
return false;
}
private static boolean isJavaProject( final IProject pj )
{
try
{
return pj.getNature( JavaCore.NATURE_ID ) != null;
}
catch( CoreException e )
{
return false;
}
}
private static boolean isFlexibleProject( final IProject pj )
{
try
{
return pj.getNature( IModuleConstants.MODULE_NATURE_ID ) != null;
}
catch( CoreException e )
{
return false;
}
}
private static boolean isJarFile( final IResource f )
{
if( f.getType() == IResource.FILE )
{
final String fname = f.getName();
if( fname.endsWith( ".jar" ) || fname.endsWith( ".zip" ) ) //$NON-NLS-1$ //$NON-NLS-2$
{
return true;
}
}
return false;
}
private static boolean isMetadataFile( final IResource f )
{
if( f.getType() == IResource.FILE )
{
final String fname = f.getName();
if( fname.equals( ".component" ) || //$NON-NLS-1$
fname.equals( "org.eclipse.wst.common.component" ) || //$NON-NLS-1$
fname.equals( ".classpath")) //$NON-NLS-1$
{
return true;
}
}
return false;
}
private static boolean isFlexibleProjectContainer( final IPath path )
{
synchronized( containerTypes )
{
return containerTypes.contains( path.segment( 0 ) );
}
}
private static void addFlexibleProjectContainerType( final String type )
{
synchronized( containerTypes )
{
containerTypes.add( type );
}
}
private static final class Listener
implements IResourceChangeListener
{
public static void register()
{
final Listener listener = new Listener();
final IWorkspace ws = ResourcesPlugin.getWorkspace();
ws.addResourceChangeListener( listener, IResourceChangeEvent.PRE_BUILD );
}
public void resourceChanged( final IResourceChangeEvent event )
{
// Screen the delta before going any further.
if( ! isInterestingEvent( event ) )
{
return;
}
// Locate all of the flexible project containers.
final ArrayList containers = new ArrayList();
final IProject[] projects
= ResourcesPlugin.getWorkspace().getRoot().getProjects();
for( int i = 0; i < projects.length; i++ )
{
final IProject project = projects[ i ];
try
{
if( isJavaProject( project ) )
{
final IJavaProject jproj = JavaCore.create( project );
final IClasspathEntry[] cpes = jproj.getRawClasspath();
for( int j = 0; j < cpes.length; j++ )
{
final IClasspathEntry cpe = cpes[ j ];
final IPath path = cpe.getPath();
if( cpe.getEntryKind() == IClasspathEntry.CPE_CONTAINER &&
isFlexibleProjectContainer( path ) )
{
final IClasspathContainer cont
= JavaCore.getClasspathContainer( path, jproj );
containers.add( cont );
}
}
}
}
catch( JavaModelException e )
{
CommonFrameworksPlugin.log( e );
}
}
// Refresh the containers that are out of date.
final IResourceDelta delta = event.getDelta();
for( int i = 0, n = containers.size(); i < n; i++ )
{
final FlexibleProjectContainer c
= (FlexibleProjectContainer) containers.get( i );
if( c.isOutOfDate( delta ) )
{
c.refresh();
}
}
}
private static boolean isInterestingEvent( final IResourceChangeEvent event )
{
final boolean[] result = new boolean[ 1 ];
final IResourceDeltaVisitor visitor = new IResourceDeltaVisitor()
{
public boolean visit( final IResourceDelta delta )
{
final IResource r = delta.getResource();
switch( r.getType() )
{
case IResource.ROOT:
{
return true;
}
case IResource.PROJECT:
{
return ModuleCoreNature.isFlexibleProject( (IProject) r );
}
case IResource.FOLDER:
{
final int kind = delta.getKind();
if( kind == IResourceDelta.ADDED ||
kind == IResourceDelta.REMOVED )
{
result[ 0 ] = true;
return false;
}
else
{
return true;
}
}
case IResource.FILE:
{
if( isJarFile( r ) || isMetadataFile( r ) )
{
result[ 0 ] = true;
}
return false;
}
default:
{
return false;
}
}
}
};
try
{
event.getDelta().accept( visitor, false );
}
catch( CoreException e )
{
CommonFrameworksPlugin.log( e );
}
return result[ 0 ];
}
}
}