blob: 0866a38b71bc01571e087ecfb8c36b8b82984c80 [file] [log] [blame]
/*
*(c) Copyright QNX Software Systems Ltd. 2002.
* All Rights Reserved.
*
*/
package org.eclipse.cdt.debug.internal.core.sourcelookup;
import java.io.IOException;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xerces.dom.DocumentImpl;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.model.IStackFrameInfo;
import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocation;
import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator;
import org.eclipse.cdt.debug.core.sourcelookup.IProjectSourceLocation;
import org.eclipse.cdt.debug.core.sourcelookup.SourceLookupFactory;
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.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IPersistableSourceLocator;
import org.eclipse.debug.core.model.IStackFrame;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
*
* Default source locator.
*
* @since Aug 19, 2002
*/
public class CSourceLocator implements ICSourceLocator, IPersistableSourceLocator, IResourceChangeListener
{
private static final String SOURCE_LOCATOR_NAME = "cSourceLocator";
private static final String DISABLED_GENERIC_PROJECT_NAME = "disabledGenericProject";
private static final String ADDITIONAL_SOURCE_LOCATION_NAME = "additionalSourceLocation";
private static final String SOURCE_LOCATION_NAME = "cSourceLocation";
private static final String ATTR_CLASS = "class";
private static final String ATTR_MEMENTO = "memento";
private static final String ATTR_PROJECT_NAME = "projectName";
private static final String ATTR_DUPLICATE_FILES = "duplicateFiles";
/**
* The project associated with this locator.
*/
private IProject fProject = null;
/**
* The array of source locations associated with this locator.
*/
private ICSourceLocation[] fSourceLocations;
/**
* The array of projects referenced by main project.
*/
private List fReferencedProjects = new ArrayList( 10 );
/**
* The flag specifies whether to search for all source elements,
* or just the first match.
*/
private boolean fDuplicateFiles = false;
/**
* Constructor for CSourceLocator.
*/
public CSourceLocator( IProject project )
{
setProject( project );
setReferencedProjects();
setSourceLocations( getDefaultSourceLocations() );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.ISourceLocator#getSourceElement(IStackFrame)
*/
public Object getSourceElement( IStackFrame stackFrame )
{
if ( stackFrame != null && stackFrame.getAdapter( IStackFrameInfo.class ) != null )
{
return getInput( (IStackFrameInfo)stackFrame.getAdapter( IStackFrameInfo.class ) );
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.ICSourceLocator#getLineNumber(IStackFrameInfo)
*/
public int getLineNumber( IStackFrame frame )
{
IStackFrameInfo info = (IStackFrameInfo)frame.getAdapter( IStackFrameInfo.class );
return ( info != null ) ? info.getFrameLineNumber() : 0;
}
protected Object getInput( IStackFrameInfo info )
{
LinkedList list = new LinkedList();
if ( info != null )
{
Object result = null;
String fileName = info.getFile();
if ( fileName != null && fileName.length() > 0 )
{
ICSourceLocation[] locations = getSourceLocations();
for ( int i = 0; i < locations.length; ++i )
{
try
{
result = locations[i].findSourceElement( fileName );
}
catch( CoreException e )
{
// do nothing
}
if ( result != null )
{
if ( result instanceof List )
list.addAll( (List)result );
else
list.add( result );
if ( !searchForDuplicateFiles() )
break;
}
}
}
}
return ( list.size() > 0 ) ? ( ( list.size() == 1 ) ? list.getFirst() : list ) : null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.ICSourceLocator#contains(IResource)
*/
public boolean contains( IResource resource )
{
ICSourceLocation[] locations = getSourceLocations();
for ( int i = 0; i < locations.length; ++i )
{
if ( resource instanceof IProject )
{
if ( locations[i] instanceof CProjectSourceLocation &&
((CProjectSourceLocation)locations[i]).getProject().equals( resource ) )
{
return true;
}
}
if ( resource instanceof IFile )
{
try
{
Object result = locations[i].findSourceElement( resource.getLocation().toOSString() );
if ( result instanceof IFile && ((IFile)result).equals( resource ) )
return true;
if ( result instanceof List && ((List)result).contains( resource ) )
return true;
}
catch( CoreException e )
{
}
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator#getSourceLocations()
*/
public ICSourceLocation[] getSourceLocations()
{
return fSourceLocations;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator#setSourceLocations(ICSourceLocation[])
*/
public void setSourceLocations( ICSourceLocation[] locations )
{
fSourceLocations = locations;
}
/**
* Returns a default collection of source locations for
* the given project. Default source locations consist
* of the given project and all of its referenced projects.
*
* @param project a project
* @return a collection of source locations for all required
* projects
* @exception CoreException
*/
public static ICSourceLocation[] getDefaultSourceLocations( IProject project )
{
ArrayList list = new ArrayList();
if ( project != null && project.exists() )
{
list.add( SourceLookupFactory.createProjectSourceLocation( project ) );
addReferencedSourceLocations( list, project );
}
return (ICSourceLocation[])list.toArray( new ICSourceLocation[list.size()] );
}
private static void addReferencedSourceLocations( List list, IProject project )
{
if ( project != null )
{
try
{
IProject[] projects = project.getReferencedProjects();
for ( int i = 0; i < projects.length; i++ )
{
if ( projects[i].exists() && !containsProject( list, projects[i] ) )
{
list.add( SourceLookupFactory.createProjectSourceLocation( projects[i] ) );
addReferencedSourceLocations( list, projects[i] );
}
}
}
catch( CoreException e )
{
// do nothing
}
}
}
private static boolean containsProject( List list, IProject project )
{
Iterator it = list.iterator();
while( it.hasNext() )
{
CProjectSourceLocation location = (CProjectSourceLocation)it.next();
if ( project.equals( location.getProject() ) )
return true;
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator#findSourceElement(String)
*/
public Object findSourceElement( String fileName )
{
Object result = null;
if ( fileName != null && fileName.length() > 0 )
{
ICSourceLocation[] locations = getSourceLocations();
for ( int i = 0; i < locations.length; ++i )
{
try
{
result = locations[i].findSourceElement( fileName );
}
catch( CoreException e )
{
// do nothing
}
if ( result != null )
break;
}
}
return result;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IPersistableSourceLocator#getMemento()
*/
public String getMemento() throws CoreException
{
Document doc = new DocumentImpl();
Element node = doc.createElement( SOURCE_LOCATOR_NAME );
doc.appendChild( node );
ICSourceLocation[] locations = getSourceLocations();
saveDisabledGenericSourceLocations( locations, doc, node );
saveAdditionalSourceLocations( locations, doc, node );
node.setAttribute( ATTR_DUPLICATE_FILES, new Boolean( searchForDuplicateFiles() ).toString() );
try
{
return CDebugUtils.serializeDocument( doc, " " );
}
catch( IOException e )
{
abort( "Unable to create memento for C/C++ source locator.", e );
}
// execution will not reach here
return null;
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IPersistableSourceLocator#initializeDefaults(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void initializeDefaults( ILaunchConfiguration configuration ) throws CoreException
{
setSourceLocations( getDefaultSourceLocations() );
}
/* (non-Javadoc)
* @see org.eclipse.debug.core.model.IPersistableSourceLocator#initializeFromMemento(java.lang.String)
*/
public void initializeFromMemento( String memento ) throws CoreException
{
Exception ex = null;
try
{
Element root = null;
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
StringReader reader = new StringReader( memento );
InputSource source = new InputSource( reader );
root = parser.parse( source ).getDocumentElement();
if ( !root.getNodeName().equalsIgnoreCase( SOURCE_LOCATOR_NAME ) )
{
abort( "Unable to restore C/C++ source locator - invalid format.", null );
}
List sourceLocations = new ArrayList();
// Add locations based on referenced projects
IProject project = getProject();
if ( project != null && project.exists() && project.isOpen() )
sourceLocations.addAll( Arrays.asList( getDefaultSourceLocations() ) );
removeDisabledLocations( root, sourceLocations );
addAdditionalLocations( root, sourceLocations );
// To support old launch configuration
addOldLocations( root, sourceLocations );
setSourceLocations( (ICSourceLocation[])sourceLocations.toArray( new ICSourceLocation[sourceLocations.size()] ) );
setSearchForDuplicateFiles( Boolean.valueOf( root.getAttribute( ATTR_DUPLICATE_FILES ) ).booleanValue() );
return;
}
catch( ParserConfigurationException e )
{
ex = e;
}
catch( SAXException e )
{
ex = e;
}
catch( IOException e )
{
ex = e;
}
abort( "Exception occurred initializing source locator.", ex );
}
private void removeDisabledLocations( Element root, List sourceLocations ) throws CoreException
{
NodeList list = root.getChildNodes();
int length = list.getLength();
HashSet disabledProjects = new HashSet( length );
for ( int i = 0; i < length; ++i )
{
Node node = list.item( i );
short type = node.getNodeType();
if ( type == Node.ELEMENT_NODE )
{
Element entry = (Element)node;
if ( entry.getNodeName().equalsIgnoreCase( DISABLED_GENERIC_PROJECT_NAME ) )
{
String projectName = entry.getAttribute( ATTR_PROJECT_NAME );
if ( isEmpty( projectName ) )
{
CDebugCorePlugin.log( "Unable to restore C/C++ source locator - invalid format." );
}
disabledProjects.add( projectName.trim() );
}
}
}
Iterator it = sourceLocations.iterator();
while( it.hasNext() )
{
ICSourceLocation location = (ICSourceLocation)it.next();
if ( location instanceof IProjectSourceLocation &&
disabledProjects.contains( ((IProjectSourceLocation)location).getProject().getName() ) )
it.remove();
}
}
private void addAdditionalLocations( Element root, List sourceLocations ) throws CoreException
{
ClassLoader classLoader = CDebugCorePlugin.getDefault() .getDescriptor().getPluginClassLoader();
MultiStatus status = new MultiStatus( CDebugCorePlugin.getUniqueIdentifier(),
CDebugCorePlugin.INTERNAL_ERROR,
"Error initializing directory source location.",
null );
NodeList list = root.getChildNodes();
int length = list.getLength();
for ( int i = 0; i < length; ++i )
{
Node node = list.item( i );
short type = node.getNodeType();
if ( type == Node.ELEMENT_NODE )
{
Element entry = (Element)node;
if ( entry.getNodeName().equalsIgnoreCase( ADDITIONAL_SOURCE_LOCATION_NAME ) )
{
String className = entry.getAttribute( ATTR_CLASS );
String data = entry.getAttribute( ATTR_MEMENTO );
if ( isEmpty( className ) )
{
CDebugCorePlugin.log( "Unable to restore C/C++ source locator - invalid format." );
continue;
}
Class clazz = null;
try
{
clazz = classLoader.loadClass( className );
}
catch( ClassNotFoundException e )
{
CDebugCorePlugin.log( MessageFormat.format( "Unable to restore source location - class not found {0}", new String[] { className } ) );
continue;
}
ICSourceLocation location = null;
try
{
location = (ICSourceLocation)clazz.newInstance();
}
catch( IllegalAccessException e )
{
CDebugCorePlugin.log( "Unable to restore source location." );
continue;
}
catch( InstantiationException e )
{
CDebugCorePlugin.log( "Unable to restore source location." );
continue;
}
try
{
location.initializeFrom( data );
sourceLocations.add( location );
}
catch( CoreException e )
{
status.addAll( e.getStatus() );
}
}
}
}
if ( status.getSeverity() > IStatus.OK )
throw new CoreException( status );
}
private void addOldLocations( Element root, List sourceLocations ) throws CoreException
{
ClassLoader classLoader = CDebugCorePlugin.getDefault() .getDescriptor().getPluginClassLoader();
NodeList list = root.getChildNodes();
int length = list.getLength();
for ( int i = 0; i < length; ++i )
{
Node node = list.item( i );
short type = node.getNodeType();
if ( type == Node.ELEMENT_NODE )
{
Element entry = (Element)node;
if ( entry.getNodeName().equalsIgnoreCase( SOURCE_LOCATION_NAME ) )
{
String className = entry.getAttribute( ATTR_CLASS );
String data = entry.getAttribute( ATTR_MEMENTO );
if ( isEmpty( className ) )
{
CDebugCorePlugin.log( "Unable to restore C/C++ source locator - invalid format." );
continue;
}
Class clazz = null;
try
{
clazz = classLoader.loadClass( className );
}
catch( ClassNotFoundException e )
{
CDebugCorePlugin.log( MessageFormat.format( "Unable to restore source location - class not found {0}", new String[] { className } ) );
continue;
}
ICSourceLocation location = null;
try
{
location = (ICSourceLocation)clazz.newInstance();
}
catch( IllegalAccessException e )
{
CDebugCorePlugin.log( "Unable to restore source location." );
continue;
}
catch( InstantiationException e )
{
CDebugCorePlugin.log( "Unable to restore source location." );
continue;
}
location.initializeFrom( data );
if ( !sourceLocations.contains( location ) )
{
if ( location instanceof CProjectSourceLocation )
((CProjectSourceLocation)location).setGenerated( isReferencedProject( ((CProjectSourceLocation)location).getProject() ) );
sourceLocations.add( location );
}
}
}
}
}
/**
* Throws an internal error exception
*/
private void abort( String message, Throwable e ) throws CoreException
{
IStatus s = new Status( IStatus.ERROR,
CDebugCorePlugin.getUniqueIdentifier(),
CDebugCorePlugin.INTERNAL_ERROR,
message,
e );
throw new CoreException( s );
}
private boolean isEmpty( String string )
{
return string == null || string.trim().length() == 0;
}
public void resourceChanged( IResourceChangeEvent event )
{
if ( event.getSource() instanceof IWorkspace && event.getDelta() != null )
{
IResourceDelta[] deltas = event.getDelta().getAffectedChildren();
if ( deltas != null )
{
ArrayList list = new ArrayList( deltas.length );
for ( int i = 0; i < deltas.length; ++i )
if ( deltas[i].getResource() instanceof IProject )
list.add( deltas[i].getResource() );
resetSourceLocations( list );
}
}
}
private void saveDisabledGenericSourceLocations( ICSourceLocation[] locations, Document doc, Element node )
{
IProject project = getProject();
if ( project != null && project.exists() && project.isOpen() )
{
List list = CDebugUtils.getReferencedProjects( project );
HashSet names = new HashSet( list.size() + 1 );
names.add( project.getName() );
Iterator it = list.iterator();
while( it.hasNext() )
{
names.add( ((IProject)it.next()).getName() );
}
for ( int i = 0; i < locations.length; ++i )
if ( locations[i] instanceof IProjectSourceLocation &&
((IProjectSourceLocation)locations[i]).isGeneric() )
names.remove( ((IProjectSourceLocation)locations[i]).getProject().getName() );
it = names.iterator();
while ( it.hasNext() )
{
Element child = doc.createElement( DISABLED_GENERIC_PROJECT_NAME );
child.setAttribute( ATTR_PROJECT_NAME, (String)it.next() );
node.appendChild( child );
}
}
}
private void saveAdditionalSourceLocations( ICSourceLocation[] locations, Document doc, Element node )
{
for ( int i = 0; i < locations.length; i++ )
{
if ( locations[i] instanceof IProjectSourceLocation &&
((IProjectSourceLocation)locations[i]).isGeneric() )
continue;
Element child = doc.createElement( ADDITIONAL_SOURCE_LOCATION_NAME );
child.setAttribute( ATTR_CLASS, locations[i].getClass().getName() );
try
{
child.setAttribute( ATTR_MEMENTO, locations[i].getMemento() );
}
catch( CoreException e )
{
CDebugCorePlugin.log( e );
continue;
}
node.appendChild( child );
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator#getProject()
*/
public IProject getProject()
{
return fProject;
}
protected void setProject( IProject project )
{
fProject = project;
}
private boolean isReferencedProject( IProject ref )
{
if ( getProject() != null )
{
try
{
return Arrays.asList( getProject().getReferencedProjects() ).contains( ref );
}
catch( CoreException e )
{
CDebugCorePlugin.log( e );
}
}
return false;
}
private void setReferencedProjects()
{
fReferencedProjects.clear();
fReferencedProjects = CDebugUtils.getReferencedProjects( getProject() );
}
protected ICSourceLocation[] getDefaultSourceLocations()
{
Iterator it = fReferencedProjects.iterator();
ArrayList list = new ArrayList( fReferencedProjects.size() );
if ( getProject() != null && getProject().exists() && getProject().isOpen() )
list.add( SourceLookupFactory.createProjectSourceLocation( getProject() ) );
while( it.hasNext() )
{
IProject project = (IProject)it.next();
if ( project != null && project.exists() && project.isOpen() )
list.add( SourceLookupFactory.createProjectSourceLocation( project ) );
}
return (ICSourceLocation[])list.toArray( new ICSourceLocation[list.size()] );
}
private void resetSourceLocations( List affectedProjects )
{
if ( affectedProjects.size() != 0 && getProject() != null )
{
if ( !getProject().exists() || !getProject().isOpen() )
{
removeGenericSourceLocations();
}
else
{
updateGenericSourceLocations( affectedProjects );
}
}
}
private void removeGenericSourceLocations()
{
fReferencedProjects.clear();
ICSourceLocation[] locations = getSourceLocations();
ArrayList newLocations = new ArrayList( locations.length );
for ( int i = 0; i < locations.length; ++i )
if ( !(locations[i] instanceof IProjectSourceLocation) || !((IProjectSourceLocation)locations[i]).isGeneric() )
newLocations.add( locations[i] );
setSourceLocations( (ICSourceLocation[])newLocations.toArray( new ICSourceLocation[newLocations.size()] ) );
}
private void updateGenericSourceLocations( List affectedProjects )
{
List newRefs = CDebugUtils.getReferencedProjects( getProject() );
ICSourceLocation[] locations = getSourceLocations();
ArrayList newLocations = new ArrayList( locations.length );
for ( int i = 0; i < locations.length; ++i )
{
if ( !(locations[i] instanceof IProjectSourceLocation) || !((IProjectSourceLocation)locations[i]).isGeneric() )
{
newLocations.add( locations[i] );
}
else
{
IProject project = ((IProjectSourceLocation)locations[i]).getProject();
if ( project.exists() && project.isOpen() )
{
if ( newRefs.contains( project ) || project.equals( getProject() ) )
{
newLocations.add( locations[i] );
newRefs.remove( project );
}
}
}
}
Iterator it = newRefs.iterator();
while( it.hasNext() )
{
IProject project = (IProject)it.next();
if ( !fReferencedProjects.contains( project ) )
newLocations.add( SourceLookupFactory.createProjectSourceLocation( project ) );
}
fReferencedProjects = newRefs;
setSourceLocations( (ICSourceLocation[])newLocations.toArray( new ICSourceLocation[newLocations.size()] ) );
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator#searchForDuplicateFiles()
*/
public boolean searchForDuplicateFiles()
{
return fDuplicateFiles;
}
public void setSearchForDuplicateFiles( boolean search )
{
fDuplicateFiles = search;
ICSourceLocation[] locations = getSourceLocations();
for ( int i = 0; i < locations.length; ++i )
locations[i].setSearchForDuplicateFiles( search );
}
}