/*******************************************************************************
* Copyright © 2012, 2013 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.debug.core.java.filters;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.edt.debug.core.EDTDebugCorePlugin;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
/**
* A filter that uses the classpath of a project from within the workspace. Currently only changes to source entries are updated in the cache.
*/
public abstract class WorkspaceProjectClassFilter extends ClasspathEntryFilter implements IElementChangedListener
{
public WorkspaceProjectClassFilter()
{
JavaCore.addElementChangedListener( this, ElementChangedEvent.POST_CHANGE );
}
@Override
public void dispose()
{
JavaCore.removeElementChangedListener( this );
super.dispose();
}
@Override
protected IClasspathEntry[] getCommonClasspathEntries()
{
String[] projects = getProjectNames();
if ( projects != null && projects.length > 0 )
{
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
List<IClasspathEntry> list = new ArrayList<IClasspathEntry>();
for ( String project : projects )
{
IProject proj = root.getProject( project );
try
{
if ( proj.isAccessible() && proj.hasNature( JavaCore.NATURE_ID ) )
{
for ( IClasspathEntry entry : JavaCore.create( proj ).getResolvedClasspath( true ) )
{
switch ( entry.getEntryKind() )
{
case IClasspathEntry.CPE_LIBRARY:
if ( includeLibraries() )
{
list.add( entry );
}
break;
case IClasspathEntry.CPE_PROJECT:
if ( includeReferencedProjects() )
{
list.add( entry );
}
break;
case IClasspathEntry.CPE_CONTAINER:
if ( includeContainers() )
{
list.add( entry );
}
break;
case IClasspathEntry.CPE_SOURCE:
if ( includeSource() )
{
list.add( entry );
}
break;
}
}
}
}
catch ( CoreException ce )
{
EDTDebugCorePlugin.log( ce );
}
}
return list.toArray( new IClasspathEntry[ list.size() ] );
}
return null;
}
@Override
public void elementChanged( ElementChangedEvent event )
{
if ( event.getDelta() != null )
{
String[] projectNames = getProjectNames();
for ( IJavaElementDelta kid : event.getDelta().getAffectedChildren() )
{
if ( kid.getElement() != null && kid.getElement().getElementType() == IJavaElement.JAVA_PROJECT && kid.getAffectedChildren() == null
|| kid.getAffectedChildren().length == 0 )
{
// When a monitored project is deleted or added, recalculate the cache. A rename will come in as delete & add.
if ( kid.getKind() == IJavaElementDelta.ADDED || kid.getKind() == IJavaElementDelta.REMOVED )
{
for ( int i = 0; i < projectNames.length; i++ )
{
if ( projectNames[ i ].equals( kid.getElement().getElementName() ) )
{
commonClassesToFilter = new HashMap<String, Object>( 100 );
try
{
processEntries( getCommonClasspathEntries(), (IJavaProject)kid.getElement(), commonClassesToFilter );
}
catch ( CoreException ce )
{
EDTDebugCorePlugin.log( ce );
}
// We've completely recalculated, no other deltas matter.
return;
}
}
}
}
else
{
IResource resource = kid.getElement().getResource();
if ( resource != null )
{
for ( int i = 0; i < projectNames.length; i++ )
{
if ( projectNames[ i ].equals( resource.getProject().getName() ) )
{
processDelta( kid );
break;
}
}
}
}
}
}
}
protected void processDelta( IJavaElementDelta delta )
{
IJavaElementDelta[] kids = delta.getAffectedChildren();
if ( kids == null || kids.length == 0 )
{
IJavaElement element = delta.getElement();
if ( element != null && element.getElementType() == IJavaElement.COMPILATION_UNIT )
{
try
{
switch ( delta.getKind() )
{
case IJavaElementDelta.ADDED:
compilationUnitAdded( delta, (ICompilationUnit)delta.getElement() );
break;
case IJavaElementDelta.REMOVED:
compilationUnitRemoved( delta, (ICompilationUnit)delta.getElement() );
break;
case IJavaElementDelta.CHANGED:
compilationUnitChanged( delta, (ICompilationUnit)delta.getElement() );
break;
}
}
catch ( JavaModelException jme )
{
EDTDebugCorePlugin.log( jme );
}
}
else if ( element != null && element.getElementType() == IJavaElement.PACKAGE_FRAGMENT )
{
try
{
switch ( delta.getKind() )
{
case IJavaElementDelta.ADDED:
packageFragmentAdded( delta, (IPackageFragment)element );
break;
case IJavaElementDelta.REMOVED:
packageFragmentRemoved( delta, (IPackageFragment)element );
break;
}
}
catch ( JavaModelException jme )
{
EDTDebugCorePlugin.log( jme );
}
}
}
else
{
for ( int i = 0; i < kids.length; i++ )
{
processDelta( kids[ i ] );
}
}
}
protected void compilationUnitAdded( IJavaElementDelta delta, ICompilationUnit cu ) throws JavaModelException
{
for ( IType type : cu.getAllTypes() )
{
commonClassesToFilter.put( type.getFullyQualifiedName(), null );
}
}
protected void compilationUnitRemoved( IJavaElementDelta delta, ICompilationUnit cu ) throws JavaModelException
{
if ( cu.exists() )
{
for ( IType type : cu.getAllTypes() )
{
commonClassesToFilter.remove( type.getFullyQualifiedName() );
}
}
else
{
// It's already been deleted, so we can't get any of its children. We'll remove the main type, then iterate over the keys
// to see if there's any inner classes to remove.
StringBuilder buf = new StringBuilder( 50 );
IJavaElement current = cu.getParent();
while ( current != null && current.getElementType() == IJavaElement.PACKAGE_FRAGMENT )
{
if ( buf.length() > 0 )
{
buf.insert( 0, '.' );
}
buf.insert( 0, current.getElementName() );
current = current.getParent();
}
if ( buf.length() > 0 )
{
buf.append( '.' );
}
buf.append( cu.getElementName().substring( 0, cu.getElementName().length() - 5 ) ); // Remove ".java"
commonClassesToFilter.remove( buf.toString() );
buf.append( '$' );
String keyPrefix = buf.toString();
for ( Iterator<String> it = commonClassesToFilter.keySet().iterator(); it.hasNext(); )
{
if ( it.next().startsWith( keyPrefix ) )
{
it.remove();
}
}
}
}
protected void compilationUnitChanged( IJavaElementDelta delta, ICompilationUnit cu ) throws JavaModelException
{
if ( (delta.getFlags() & IJavaElementDelta.F_PRIMARY_RESOURCE) != 0 )
{
// Inner classes might have changed - remove and then re-add.
compilationUnitRemoved( delta, cu );
compilationUnitAdded( delta, cu );
}
}
protected void packageFragmentAdded( IJavaElementDelta delta, IPackageFragment fragment ) throws JavaModelException
{
for ( ICompilationUnit cu : fragment.getCompilationUnits() )
{
compilationUnitAdded( delta, cu );
}
}
protected void packageFragmentRemoved( IJavaElementDelta delta, IPackageFragment fragment ) throws JavaModelException
{
if ( fragment.exists() )
{
for ( ICompilationUnit cu : fragment.getCompilationUnits() )
{
compilationUnitRemoved( delta, cu );
}
}
else
{
// It's already been deleted, so we can't get any of its children. We'll iterate over the keys removing those inside this fragment.
StringBuilder buf = new StringBuilder( 50 );
IJavaElement current = fragment;
while ( current != null && current.getElementType() == IJavaElement.PACKAGE_FRAGMENT )
{
if ( buf.length() > 0 )
{
buf.insert( 0, '.' );
}
buf.insert( 0, current.getElementName() );
current = current.getParent();
}
if ( buf.length() > 0 )
{
buf.append( '.' );
String keyPrefix = buf.toString();
for ( Iterator<String> it = commonClassesToFilter.keySet().iterator(); it.hasNext(); )
{
if ( it.next().startsWith( keyPrefix ) )
{
it.remove();
}
}
}
}
}
/**
* @return true if dependent project classpaths should be included; default is false, and subclasses may override this.
*/
protected boolean includeReferencedProjects()
{
return false;
}
/**
* @return true if library classpath entries should be included; default is false, and subclasses may override this.
*/
protected boolean includeLibraries()
{
return false;
}
/**
* @return true if container classpath entries should be included; default is false, and subclasses may override this.
*/
protected boolean includeContainers()
{
return false;
}
/**
* @return true if source classpath entries should be included; default is true, and subclasses may override this.
*/
protected boolean includeSource()
{
return true;
}
/**
* @return the names of the workspace projects whose classes should be filtered.
*/
public abstract String[] getProjectNames();
}