/**
* 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.style.stylishhaskell;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.eclipsefp.haskell.style.StylePlugin;
import net.sf.eclipsefp.haskell.util.FileUtil;
import net.sf.eclipsefp.haskell.util.ProcessRunner;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
/**
* Utilities to invoke stylish-haskell and manage its configuration files
* @author JP Moresmau
*
*/
public class StylishHaskell {
/**
* run stylish haskell
* @param exe the path to the exe (may not be full if we hope it's in the path)
* @param project the project we're in
* @param filePath the file to format
* @param charset the charset of the file
* @param extensions the Haskell extensions needed to parse the file correctly
* @throws Exception
*/
public static void runStylishHaskell(String exe, IProject project, File filePath,String charset,Set<String> extensions) throws Exception{
List<String> cmd=new ArrayList<>();
cmd.add(exe);
cmd.add("-i"); // in place
String cf=getConfigFile(project);
File tempFile=null;
if (cf!=null){
// add the required extensions
if (extensions!=null && extensions.size()>0){
SHConfiguration conf=load(cf);
conf.getLanguageExtensions().addAll(extensions);
tempFile=File.createTempFile("stylish", ".yaml");
save(conf,tempFile);
cf=tempFile.getAbsolutePath();
}
cmd.add("--config="+cf);
}
try {
cmd.add(filePath.getAbsolutePath());
// keep old contents
String contents=FileUtil.getContents(filePath, charset);
// capture errors
try (StringWriter swErr=new StringWriter();
StringWriter swOut=new StringWriter()) {
int code=new ProcessRunner().executeBlocking(filePath.getParentFile(), swOut, swErr,cmd.toArray(new String[cmd.size()]));
String err=swErr.toString();
if (code!=0 || err.length()>0){
// we restore the file contents if we failed
FileUtil.writeSharedFile(filePath, contents, 1);
throw new IOException(err);
}
}
} finally {
if (tempFile!=null){
tempFile.delete();
}
}
}
/**
* get the path of the config file to use for a given project, or null to use defaults
* @param project the project
* @return
*/
public static String getConfigFile(IProject project){
IFile f=project.getFile(".stylish-haskell.yaml");
if (f!=null && f.exists()){
return f.getLocation().toOSString();
}
IPath pluginp=StylePlugin.getStylePlugin().getStateLocation().append(".stylish-haskell.yaml");
File jf=pluginp.toFile();
if (jf.exists() && jf.isFile()){
return jf.getAbsolutePath();
}
return null;
}
/**
* has this project a specific config?
* @param project
* @return
*/
public static boolean hasProjectConfiguration(IProject project){
IFile f=project.getFile(".stylish-haskell.yaml");
return f!=null && f.exists();
}
/**
* get the configuration to display for a project
* @param project
* @return the configuration, non null (may be the default)
*/
public static SHConfiguration getProjectConfiguration(IProject project){
IFile f=project.getFile(".stylish-haskell.yaml");
if (f!=null && f.exists()){
try {
return load(f.getLocation().toOSString());
} catch (IOException ioe){
StylePlugin.logError(ioe);
}
}
return getWorkspaceConfiguration();
}
/**
* set the project configuration
* @param conf the configuration, or null to not use a project-specific config
* @param project
* @throws Exception
*/
public static void setProjectConfiguration(SHConfiguration conf,IProject project) throws Exception{
IFile f=project.getFile(".stylish-haskell.yaml");
if (conf!=null){
try {
try (ByteArrayOutputStream os=new ByteArrayOutputStream()) {
save(conf, os);
if (!f.exists()){
f.create(new ByteArrayInputStream(os.toByteArray()), true, new NullProgressMonitor());
} else {
f.setContents(new ByteArrayInputStream(os.toByteArray()), true, true, new NullProgressMonitor());
}
}
} catch (IOException ioe){
StylePlugin.logError(ioe);
throw ioe;
}
} else if (f.exists()){
try {
f.delete(true, new NullProgressMonitor());
} catch (CoreException ce){
StylePlugin.logError(ce);
throw ce;
}
}
project.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
}
/**
* get the workspace configuration
* @return the configuration, non null (may be the default)
*/
public static SHConfiguration getWorkspaceConfiguration(){
IPath pluginp=StylePlugin.getStylePlugin().getStateLocation().append(".stylish-haskell.yaml");
File jf=pluginp.toFile();
if (jf.exists() && jf.isFile()){
try {
return load(jf.getAbsolutePath());
} catch (IOException ioe){
StylePlugin.logError(ioe);
}
}
return new SHConfiguration();
}
/**
* set the workspace configuration
* @param conf
* @throws IOException
*/
public static void setWorkspaceConfiguration(SHConfiguration conf) throws IOException{
IPath pluginp=StylePlugin.getStylePlugin().getStateLocation().append(".stylish-haskell.yaml");
File jf=pluginp.toFile();
jf.getParentFile().mkdirs();
try (OutputStream os=new BufferedOutputStream(new FileOutputStream(jf))) {
save(conf, os);
} catch (IOException ioe){
StylePlugin.logError(ioe);
throw ioe;
}
}
/**
* load YAML from a file
* @param fileLocation
* @return
* @throws IOException
*/
public static SHConfiguration load(String fileLocation) throws IOException{
File f=new File(fileLocation);
try (InputStream is=new BufferedInputStream(new FileInputStream(f))) {
SHConfiguration config=load(is);
return config;
}
}
/**
* load YAML from a stream
* @param is
* @return
* @throws IOException
*/
public static SHConfiguration load(InputStream is) throws IOException{
Yaml y=new Yaml();
Object o=y.load(is);
SHConfiguration config=new SHConfiguration();
config.fromYAML(o);
return config;
}
/**
* save to a stream as YAML
* @param config
* @param os
* @throws IOException
*/
public static void save(SHConfiguration config,OutputStream os) throws IOException {
Map<String,Object> o=config.toYAML();
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);
String s=yaml.dump(o);
os.write(s.getBytes()); // platform encoding
}
/**
* save to a file as YAML
* @param config
* @param f
* @throws IOException
*/
public static void save(SHConfiguration config,File f) throws IOException {
if (f.getParentFile()!=null){
f.getParentFile().mkdirs();
}
try (OutputStream os=new BufferedOutputStream(new FileOutputStream(f))) {
save(config, os);
}
}
}