/** * Copyright (c) 2012 by JP Moresmau * This code is made available under the terms of the Eclipse Public License, * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html */ package net.sf.eclipsefp.haskell.ui.internal.backend; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import net.sf.eclipsefp.haskell.buildwrapper.types.CabalImplDetails; import net.sf.eclipsefp.haskell.buildwrapper.types.CabalImplDetails.SandboxType; import net.sf.eclipsefp.haskell.core.HaskellCorePlugin; import net.sf.eclipsefp.haskell.core.cabal.CabalImplementationManager; import net.sf.eclipsefp.haskell.core.cabal.CabalPackageRef; import net.sf.eclipsefp.haskell.core.cabal.CabalPackageVersion; import net.sf.eclipsefp.haskell.core.util.ResourceUtil; import net.sf.eclipsefp.haskell.util.PlatformUtil; import net.sf.eclipsefp.haskell.util.ProcessRunner; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IStatus; /** * helper wrapping cabal list and cabal info operations * @author JP Moresmau * */ public class CabalPackageHelper { private final String cabalPath; private List<CabalPackageRef> installed=null; private List<CabalPackageRef> all=null; /** singleton **/ private static CabalPackageHelper instance; public static synchronized CabalPackageHelper getInstance(){ if (instance==null || instance.getCabalPath()==null || !instance.getCabalPath().equals( CabalImplementationManager.getCabalExecutable() )){ instance=new CabalPackageHelper( CabalImplementationManager.getCabalExecutable() ); } return instance; } private CabalPackageHelper( final String cabalPath ) { super(); this.cabalPath = cabalPath; } public void clear(){ installed=null; all=null; } public String getCabalPath() { return cabalPath; } public boolean hasInstalledVersion(final String name,final String version) throws IOException{ String s=getLastInstalledVersion(name); if (s!=null){ return CabalPackageVersion.compare( s, version )>=0; } return false; } /** * return the version string of the last installed version * @param name the package name * @return * @throws IOException */ public String getLastInstalledVersion(final String name)throws IOException{ List<CabalPackageRef> r=list(name,true); if (r.size()>0){ for (CabalPackageRef ref:r){ if (ref.getName().equals( name )){ if (ref.getVersions().size()>0){ return ref.getVersions().get( ref.getVersions().size()-1 ); } } } } return null; } public List<CabalPackageRef> getInstalled()throws IOException { if (installed==null){ installed=list("",true); //$NON-NLS-1$ for (CabalPackageRef r:installed){ r.getInstalled().addAll( r.getVersions() ); // all versions are installed } } return installed; } public List<CabalPackageRef> getAll()throws IOException { if (all==null){ all=list("",false); //$NON-NLS-1$ // check which versions are installed List<CabalPackageRef> installed=getInstalled(); Map<String,CabalPackageRef> pkgByName=new HashMap<>(); for (CabalPackageRef r:installed){ pkgByName.put( r.getName(),r ); } for (CabalPackageRef r:all){ CabalPackageRef inst=pkgByName.get( r.getName() ); if (inst!=null){ r.getInstalled().addAll( inst.getInstalled() ); } } } return all; } public String getInfo(final String name)throws IOException { try (BufferedReader br=run(cabalPath,"info",name)) { //$NON-NLS-1$ StringBuilder sb=new StringBuilder(); String line=br.readLine(); while (line!=null){ sb.append(line); sb.append( PlatformUtil.NL ); line=br.readLine(); } return sb.toString().substring( 2 ); // starts with *<space> } } private List<CabalPackageRef> list(final String pkg,final boolean installedOnly)throws IOException{ List<CabalPackageRef> ret=new LinkedList<>(); if (cabalPath==null){ return ret; } String opt=installedOnly?"--installed":""; try (BufferedReader br=run(cabalPath,"list",pkg,opt,"--simple-output")) { //$NON-NLS-1$//$NON-NLS-2$ String line=br.readLine(); CabalPackageRef last=null; while (line!=null){ CabalPackageRef r=parseRef(line); if (r!=null){ if (last==null || !r.getName().equals( last.getName() )){ if (last!=null){ last.getVersions().trimToSize(); } ret.add(r); last=r; } else { last.getVersions().addAll(r.getVersions()); } } line=br.readLine(); } return ret; } } private CabalPackageRef parseRef(final String line){ if (line!=null && line.length()>0){ int ix=line.indexOf( ' ' ); String name=line.substring( 0,ix ); String version=line.substring( ix+1 ).trim(); CabalPackageRef r=new CabalPackageRef(); r.setName(name); r.getVersions().add( version ); return r; } return null; } /** * run the command and log any errors, returning a reader to the output * @param opts * @return * @throws IOException */ private BufferedReader run(final String... opts) throws IOException{ ProcessRunner pr=new ProcessRunner(); StringWriter swOut=new StringWriter(); StringWriter swErr=new StringWriter(); File dir=ResourcesPlugin.getWorkspace().getRoot().getLocation().makeAbsolute().toFile(); CabalImplDetails det=BackendManager.getCabalImplDetails(); // we list the packages in the sandbox if // 1) we are sandboxed by cabal // 2) we have a unique sandbox // 3) we have at least one project to use, since cabal list uses the config info stored in the root of the project if (det.isSandboxed() && det.getType().equals(SandboxType.CABAL) && det.isUniqueSandbox()){ List<IProject> p=ResourceUtil.listHaskellProjects(); if (p.size()>0){ dir=p.iterator().next().getLocation().makeAbsolute().toFile(); } } pr.executeBlocking( dir, swOut, swErr, opts ); String err=swErr.toString(); if (err.length()>0){ String warn="warning:";//$NON-NLS-1$ // clearly, a warning if (err.toLowerCase().startsWith( warn )){ HaskellCorePlugin.log( err.substring( warn.length() ).trim(), IStatus.WARNING ); } else { HaskellCorePlugin.log( err, IStatus.ERROR ); } } return new BufferedReader( new StringReader(swOut.toString()) ); } public void setInstalled( final List<CabalPackageRef> installed ) { this.installed = installed; } public void setAll( final List<CabalPackageRef> all ) { this.all = all; } }