// Copyright (c) 2003-2005 by Leif Frenzel - see http://leiffrenzel.de
package net.sf.eclipsefp.haskell.core.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
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.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.eclipsefp.haskell.buildwrapper.BWFacade;
import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
import net.sf.eclipsefp.haskell.buildwrapper.types.CabalPackage;
import net.sf.eclipsefp.haskell.buildwrapper.types.Component;
import net.sf.eclipsefp.haskell.buildwrapper.types.Component.ComponentType;
import net.sf.eclipsefp.haskell.core.HaskellCorePlugin;
import net.sf.eclipsefp.haskell.core.cabalmodel.CabalSyntax;
import net.sf.eclipsefp.haskell.core.cabalmodel.ModuleInclusionType;
import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescription;
import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionLoader;
import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionStanza;
import net.sf.eclipsefp.haskell.core.project.HaskellNature;
import net.sf.eclipsefp.haskell.util.FileUtil;
import org.eclipse.core.resources.IContainer;
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.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
/**
* <p>
* contains static helping functionality to work on file resources in the
* workspace.
* </p>
*
* @author Leif Frenzel
* @author JP Moresmau
*/
public class ResourceUtil {
public static boolean hasHaskellNature(final IProject p){
try {
return p!=null && p.isAccessible() && p.hasNature( HaskellNature.NATURE_ID );
} catch (CoreException ce){
HaskellCorePlugin.log( ce );
}
return false;
}
static Map<String,IFile> getExecutablesOfComponentType(final IProject project, final ComponentType type) {
Map<String,IFile> result = new HashMap<>();
if (hasHaskellNature(project) ){
/*ScionInstance instance=ScionPlugin.getScionInstance( project );
if (instance!=null){
for (Component c:instance.getComponents()){
if (c.isBuildable() && c.getType().equals( type )){
String name=FileUtil.makeExecutableName( c.getName() );
IFile f=project.getFile( ScionPlugin.DIST_FOLDER+File.separator+"build"+File.separator+c.getName()+File.separator+name ); //$NON-NLS-1$
if (f.exists()){
result.add( f );
}
}
}
}*/
BWFacade facade=BuildWrapperPlugin.getFacade( project );
if (facade!=null){
for (Component c:facade.getComponents()){
if (c.isBuildable() && c.getType().equals( type )){
IFile f = getExecutableLocation( project, c.getName() );
if (f.exists()){
result.put(c.getName(), f );
}
}
}
}
}
return result;
}
public static IFile getExecutableLocation (final IProject project, final String componentName) {
String name=FileUtil.makeExecutableName( componentName );
String exe = BWFacade.DIST_FOLDER + File.separator + "dist" + File.separator + //$NON-NLS-1$
"build" + File.separator + componentName + File.separator + name; //$NON-NLS-1$
return project.getFile( exe );
}
/**
* Return the project executable as an ArrayList, assuming that the
* project has the Haskell nature.
*/
public static Map<String,IFile> getProjectExecutables( final IProject project){
return getExecutablesOfComponentType( project, ComponentType.EXECUTABLE );
}
public static Map<String,IFile> getProjectTestSuites( final IProject project) {
return getExecutablesOfComponentType( project, ComponentType.TESTSUITE );
}
public static Map<String,IFile> getProjectBenchmarks( final IProject project) {
return getExecutablesOfComponentType( project, ComponentType.BENCHMARK );
}
// public static boolean isProjectExecutable(final IProject project, final String exeName)
// {
// boolean retval = false;
// String theExeName = FileUtil.makeExecutableName(exeName);
//
// try {
// ArrayList<IFile> executables = getProjectExecutablesArray(project);
// for (IFile iter: executables) {
// if (iter.getName().equals( theExeName )) {
// retval = true;
// }
// }
// } catch (CoreException e) {
// retval = false;
// }
//
// return retval;
// }
/**
* <p>
* returns the target executable for the passed project as resource. The
* project must have the Haskell nature.
* </p>
*/
public static IFile[] getProjectExecutablesArray( final IProject project ) {
Map<String,IFile> executables = getProjectExecutables(project);
return executables.values().toArray( new IFile[ executables.size() ] );
}
public static IFile[] getProjectTestSuitesArray( final IProject project ) {
Map<String,IFile> executables = getProjectTestSuites( project );
return executables.values().toArray( new IFile[ executables.size() ] );
}
public static IFile[] getProjectBenchmarksArray( final IProject project ) {
Map<String,IFile> executables = getProjectBenchmarks( project );
return executables.values().toArray( new IFile[ executables.size() ] );
}
/**
* Get the output folder of the Haskell project.
*
* @param project The Eclipse project object
* @return The IContainer object corresponding to the project's output
* folder.
*/
// public static IContainer getOutFolder(final IProject project)
// throws CoreException
// {
// Assert.isNotNull( project );
// Assert.isTrue(project.hasNature(HaskellNature.NATURE_ID));
//
// IHaskellProject hsProject = getHsProject(project);
// IPath outputPath = hsProject.getOutputPath();
// IContainer result;
// if (outputPath.equals(project.getProjectRelativePath())) {
// result = project;
// } else {
// result = project.getFolder(outputPath);
// }
// return result;
// }
/**
* <p>
* returns the source folder of the passed project as resource. The project
* must have the Haskell nature.
* </p>
*/
/*public static IContainer getSourceFolder(final IProject project)
{
return getHsProject(project).getSourceFolder();
}*/
public static Collection<IContainer> getSourceFolders(final IProject project){
try {
if( hasHaskellNature(project) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
Map<String,List<PackageDescriptionStanza>> stzs=pd.getStanzasBySourceDir();
Collection<IContainer> ret=new ArrayList<>();
for (String s:stzs.keySet()){
ret.add(getContainer( project,s ));
}
return ret;
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getSourceFolders:"+project, ex ); //$NON-NLS-1$
}
return Collections.emptyList();
}
public static Collection<IFile> getSourceFiles(final IContainer c){
Collection<IFile> files=new HashSet<>();
try {
for (IResource r:c.members()){
if (r instanceof IFile){
if (FileUtil.hasHaskellExtension( r )){
files.add((IFile)r);
}
} else if (r instanceof IContainer){
files.addAll(getSourceFiles( (IContainer )r));
}
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getSourceFiles:"+c.getProjectRelativePath(), ex ); //$NON-NLS-1$
}
return files;
}
/**
* <p>
* reads an input stream and returns the contents as String.
* </p>
*/
public static String readStream(final InputStream is) throws IOException {
StringBuilder sbResult = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line = br.readLine();
while (line != null) {
sbResult.append(line);
// Note: this could in some cases obscure the positions of elements
// in
// the code. It is no problem as long as all source positions we get
// from the parser are in terms of line/column, but it would make a
// difference if we got them in terms of offset/length
sbResult.append("\n"); //$NON-NLS-1$
line = br.readLine();
}
} finally {
is.close();
}
return sbResult.toString();
}
/**
* finds the corresponding resource for the specified element. This is
* element itself, if it is an IResource, or an adapter. Returns null, if no
* resource could be found.
*/
public static IResource findResource(final Object element) {
IResource result = null;
if (element instanceof IResource) {
result = (IResource) element;
} else if (element instanceof IAdaptable) {
Object adapter = ((IAdaptable) element).getAdapter(IResource.class);
if (adapter instanceof IResource) {
result = (IResource) adapter;
}
}
return result;
}
public static IResource[] getResourcesFromSelection(final ISelection selection){
if( selection != null && selection instanceof IStructuredSelection ) {
List<IResource> list = new ArrayList<>();
IStructuredSelection ssel = ( IStructuredSelection )selection;
for( Object element: ssel.toList() ) {
IResource res = ResourceUtil.findResource( element );
if( res != null ) {
list.add( res );
}
}
IResource[] ress = toResourceArray( list );
return ress;
}
return new IResource[0];
}
public static IResource[] toResourceArray( final List<IResource> list ) {
IResource[] result = new IResource[ list.size() ];
list.toArray( result );
return result;
}
public static IContainer getContainer(final IProject p,final String src){
return src.equals( "." )?p:p.getFolder( src ); //$NON-NLS-1$
}
/**
* Predicate that tests if the specified folder is one of the Haskell
* project's source folders.
*
* @return True, if the folder is a source folder in the Haskell project.
*/
public static boolean isSourceFolder( final IFolder folder ) {
IProject project = folder.getProject();
/*IHaskellProject hsProject = HaskellProjectManager.get( project );
IPath folderPath = folder.getProjectRelativePath();
return hsProject.getSourcePaths().contains( folderPath );
*/
try {
if( hasHaskellNature(project) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
for (String src:pd.getStanzasBySourceDir().keySet()){
if (getContainer(project,src).equals(folder)){
return true;
}
}
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "isSourceFolder:", ex ); //$NON-NLS-1$
}
return false;
}
public static boolean isInSourceFolder( final IFile file ) {
if( file == null || !file.isAccessible() ) {
return false;
}
// check if we're not in .buildwrapper for example
IPath p=file.getProjectRelativePath();
if (p.segmentCount()>1){
IFolder f=file.getProject().getFolder( p.segments()[0]);
if (f!=null && f.isDerived()){
return false;
}
}
Collection<IContainer> sourcePaths =ResourceUtil.getSourceFolders( file.getProject() );
for( IContainer sourcePath: sourcePaths ) {
if (sourcePath.getLocation().isPrefixOf( file.getLocation())){
return true;
}
}
return false;
}
public static boolean isInHaskellProject(final IResource resource) {
boolean result = false;
if (resource != null) {
IProject project = resource.getProject();
result = hasHaskellNature(project);
}
return result;
}
public static IContainer getSourceContainer( final IResource resource ) {
IProject project = resource.getProject();
try {
if(project.exists() && hasHaskellNature(project) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
for (String src:pd.getStanzasBySourceDir().keySet()){
if (src!=null && src.equals( "." )) { //$NON-NLS-1$
return project;
}
IFolder fldr=project.getFolder( src );
if (resource.getProjectRelativePath().toOSString().startsWith( fldr.getProjectRelativePath().toOSString() )){
return fldr;
}
}
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getSourceContainer:"+resource, ex ); //$NON-NLS-1$
}
return null;
}
public static Collection<IContainer> getAllSourceContainers( final IResource resource ) {
IProject project = resource.getProject();
Collection<IContainer> ret=new HashSet<>();
try {
if(project.exists() && hasHaskellNature(project) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
for (String src:pd.getStanzasBySourceDir().keySet()){
if (src!=null && src.equals( "." )) { //$NON-NLS-1$
ret.add(project);
} else {
IFolder fldr=project.getFolder( src );
if (resource.getProjectRelativePath().toOSString().startsWith( fldr.getProjectRelativePath().toOSString() )){
ret.add(fldr);
}
}
}
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getSourceContainer:"+resource, ex ); //$NON-NLS-1$
}
return ret;
}
public static Set<String> getImportPackages(final IFile[] files){
if (files==null || files.length==0){
return Collections.emptySet();
}
Set<String> ret=new HashSet<>();
Set<PackageDescriptionStanza> applicable=getApplicableStanzas( files );
for (PackageDescriptionStanza pds:applicable){
ret.addAll(pds.getDependentPackages());
}
return ret;
}
public static Set<String> getHiddenImportPackages(final IFile[] files){
Collection<String> ips=getImportPackages(files);
Set<String> hidden=new HashSet<>();
if (ips.size()>0){
Map<String,CabalPackage[]> pkgs=BuildWrapperPlugin.getFacade( files[0].getProject() ).getPackagesByDB();
//ScionPlugin.getScionInstance( files[0] ).getPackagesByDB();
if (pkgs!=null){
for (CabalPackage[] cps:pkgs.values()){
for (CabalPackage cp:cps){
if (cp.getComponents().length>0 && ips.contains(cp.getName()) && !cp.isExposed()){
hidden.add(cp.getName());
}
}
}
}
}
return hidden;
}
public static Collection<String> getSourceFolders(final IFile[] files){
if (files==null || files.length==0){
return Collections.emptySet();
}
Collection<String> ret=new HashSet<>();
Set<PackageDescriptionStanza> applicable=getApplicableStanzas( files );
for (PackageDescriptionStanza pds:applicable){
ret.addAll(pds.getSourceDirs());
}
return ret;
}
public static Collection<String> getApplicableListProperty(final IFile[] files,final CabalSyntax field){
if (files==null || files.length==0){
return Collections.emptySet();
}
// if we put them in a set, it messes up options that are made of two words, like -package ghc
// hopefully duplication of option will not be an issue
Collection<String> ret=new ArrayList<>();
Set<PackageDescriptionStanza> applicable=getApplicableStanzas( files );
for (PackageDescriptionStanza pds:applicable){
ret.addAll( PackageDescriptionLoader.parseList( pds.getProperties().get( field ) ) );
}
return ret;
}
public static IFile findFileFromModule(final IProject project,final String module){
try {
String path=module.replace( '.', '/' );
if(hasHaskellNature(project ) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
Map<String,List<PackageDescriptionStanza>> stzs=pd.getStanzasBySourceDir();
for (String src:stzs.keySet()){
IContainer fldr=getContainer(project,src);
for (String ext:FileUtil.haskellExtensions){
IFile file=fldr.getFile( new Path( path+"."+ext ) ); //$NON-NLS-1$
if (file.exists()){
return file;
}
}
}
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getModuleName:", ex ); //$NON-NLS-1$
}
return null;
}
public static String getModuleName(final IFile file){
IProject project = file.getProject();
Set<String> potential=new HashSet<>();
try {
if( hasHaskellNature(project ) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
Map<String,List<PackageDescriptionStanza>> stzs=pd.getStanzasBySourceDir();
if (FileUtil.hasHaskellExtension(file)){
for (String src:stzs.keySet()){
IContainer fldr=getContainer(project,src);
if (file.getProjectRelativePath().toOSString().startsWith( fldr.getProjectRelativePath().toOSString() )){
for (PackageDescriptionStanza stz:stzs.get(src)){
String module=getQualifiedModuleName( file, fldr );
ModuleInclusionType mit=stz.getModuleInclusionType( module );
if (ModuleInclusionType.MAIN.equals( mit )){
return "Main"; //$NON-NLS-1$
} else if (!ModuleInclusionType.MISSING.equals(mit )){
return module;
} else {
potential.add(module);
}
}
}
}
}
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getModuleName:", ex ); //$NON-NLS-1$
}
if (potential.size()==1){
return potential.iterator().next();
}
return ""; //$NON-NLS-1$
}
public static Set<PackageDescriptionStanza> getApplicableStanzas(final IFile[] files){
if (files==null || files.length==0){
return Collections.emptySet();
}
IProject project = files[0].getProject();
try {
if( hasHaskellNature(project) ) {
IFile f=BuildWrapperPlugin.getCabalFile( project );
PackageDescription pd=PackageDescriptionLoader.load(f);
Map<String,List<PackageDescriptionStanza>> stzs=pd.getStanzasBySourceDir();
Set<PackageDescriptionStanza> applicable=new HashSet<>();
for (IFile fi:files){
for (String src:stzs.keySet()){
IContainer fldr=getContainer(project,src);
if (fi.getProjectRelativePath().toOSString().startsWith( fldr.getProjectRelativePath().toOSString() )){
if (FileUtil.hasHaskellExtension(fi)){
for (PackageDescriptionStanza stz:stzs.get(src)){
String module=getQualifiedModuleName( fi, fldr );
if (!ModuleInclusionType.MISSING.equals( stz.getModuleInclusionType( module ) )){
applicable.add(stz);
}
}
} else {
applicable.addAll(stzs.get(src));
}
}
}
}
return applicable;
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "getApplicableStanzas:", ex ); //$NON-NLS-1$
}
return Collections.emptySet();
}
public static IPath getSourceRelativePath( final IResource resource ) {
IPath result = null;
IContainer sourceFolder = getSourceContainer( resource );
if( sourceFolder != null ) {
if( resource != null ) {
result = getSourceRelativePath( sourceFolder, resource );
}
}
return result;
}
public static IPath getSourceRelativePath( final IContainer sourceContainer,
final IResource resource ) {
IPath result = null;
IContainer resourceContainer = getContainer( resource );
IPath sourcePath = sourceContainer.getProjectRelativePath();
IPath resourcePath = resourceContainer.getProjectRelativePath();
if( sourcePath.isPrefixOf( resourcePath ) ) {
int count = sourcePath.segmentCount();
result = resourcePath.removeFirstSegments( count );
}
return result;
}
/** returns the container this resource is in (the resource itself, if it is
* a container). */
private static IContainer getContainer( final IResource resource ) {
return ( resource instanceof IContainer ) ? ( IContainer )resource
: resource.getParent();
}
/** <p>returns the path of the specified workspace file relative to the
* source folder in the Haskell project.</p>
* If not found, return the full path name
*
* @param file a workspace file, must not be <code>null</code>
*/
public static IPath getSourceFolderRelativeName( final IResource file ) {
if( file == null ) {
throw new IllegalArgumentException();
}
IPath projectRelPath = file.getProjectRelativePath();
IContainer sourceContainer = getSourceContainer( file );
IPath result = null;
if( sourceContainer != null ) {
IPath sourcePath = sourceContainer.getProjectRelativePath();
if( sourcePath.isPrefixOf( projectRelPath ) ) {
int count = sourcePath.segmentCount();
result = projectRelPath.removeFirstSegments( count );
}
}
if( result == null ) {
return file.getFullPath();
// String msg = file.getFullPath()
// + " is in no source folder in project " //$NON-NLS-1$
// + file.getProject().getName();
// throw new IllegalArgumentException( msg );
}
return result;
}
public static IFolder mkdirs( final IPath folderPath,
final IProject project ) throws CoreException {
IFolder result = project.getFolder( folderPath );
List<IFolder> parents = new ArrayList<>();
IContainer container = result;
while( container instanceof IFolder && !container.exists() ) {
parents.add( ( IFolder )container );
container = container.getParent();
}
Collections.reverse( parents );
for( IFolder folder: parents ) {
folder.create( true, true, new NullProgressMonitor() );
}
return result;
}
// helping methods
// ////////////////
// private static IHaskellProject getHsProject( final IProject project ) {
// return HaskellProjectManager.get( project );
// }
public static String getModuleName( final String fileName ) {
return fileName.substring( 0, fileName.lastIndexOf( '.' ) );
}
public static String getQualifiedModuleName (final IFile file,final IContainer source){
IPath path=file.getProjectRelativePath().removeFirstSegments( source.getProjectRelativePath().segmentCount() );
String s=path.toString();
return getModuleName(s).replace( '/', '.' );
}
public static Collection<IProject> getProjects(final ISelection arg1 ){
Set<IProject> projects=new LinkedHashSet<>();
if (arg1 instanceof IStructuredSelection){
for (Iterator<?> it=((IStructuredSelection)arg1).iterator();it.hasNext();){
IResource res = ResourceUtil.findResource( it.next() );
if( res != null && res.getProject()!=null && hasHaskellNature(res.getProject())) {
projects.add( res.getProject() );
}
}
}
return projects;
}
public static IProject[] getHaskellProjects( final IWorkspaceRoot root ) {
List<IProject> list = new ArrayList<>();
for( IProject project:root.getProjects() ) {
if( project.isOpen()
&& hasHaskellNature( project )) {
list.add( project );
}
}
IProject[] result = new IProject[ list.size() ];
list.toArray( result );
return result;
}
public static List<IProject> listHaskellProjects( ) {
IWorkspaceRoot root=ResourcesPlugin.getWorkspace().getRoot();
List<IProject> list = new ArrayList<>();
for( IProject project:root.getProjects() ) {
if(project.isOpen()
&& hasHaskellNature( project )) {
list.add( project );
}
}
return list;
}
}