blob: 3d9e2b831c3b1357c4627e1a3dce3dcab0f9ba4f [file] [log] [blame]
/**
* <copyright>
* Copyright (c) 2010-2014 Henshin developers. 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
* </copyright>
*/
package org.eclipse.emf.henshin.statespace.external;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.henshin.statespace.StateSpace;
import org.eclipse.emf.henshin.statespace.StateSpaceExporter;
import org.eclipse.emf.henshin.statespace.external.cadp.AUTStateSpaceExporter;
import org.eclipse.emf.henshin.statespace.impl.AbstractStateSpaceValidator;
/**
* Abstract state space validator implementation that is based
* on invoking external tools on files derived from the state space.
* Contains some helper methods for invoking commands and converting files.
*
* @author Christian Krause
*/
public abstract class AbstractFileBasedValidator extends AbstractStateSpaceValidator {
/**
* A key used for exported AUT files. Used for caching.
*/
public static final String AUT_FILE_EXPORT_KEY = "aut-export";
// File cache map (used internally):
private Map<StateSpace,Map<String,File>> fileCache = new HashMap<StateSpace,Map<String,File>>();
// Adapter that makes sure changed state spaces are removed from the file cache:
private Adapter cacheAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification event) {
clearCachedFiles((StateSpace) event.getNotifier());
}
};
/**
* Cache a file that is associated to a state space.
* @param stateSpace The state space.
* @param key Unique key for the file type.
* @param file The file to be cached.
*/
protected void addCachedFile(StateSpace stateSpace, String key, File file) {
Map<String,File> files = fileCache.get(stateSpace);
if (files==null) {
files = new HashMap<String,File>();
fileCache.put(stateSpace, files);
}
// Delete existing file:
if (files.containsKey(key)) {
files.get(key).delete();
}
files.put(key, file);
if (!stateSpace.eAdapters().contains(cacheAdapter)) {
stateSpace.eAdapters().add(cacheAdapter);
}
}
/**
* Get a cached file.
* @param stateSpace State space.
* @param key Unique key.
* @return Cached file.
*/
protected File getCachedFile(StateSpace stateSpace, String key) {
Map<String,File> files = fileCache.get(stateSpace);
if (files==null) {
return null;
}
File file = files.get(key);
if (file!=null && file.exists()) {
return file;
} else {
files.remove(key);
return null;
}
}
/**
* Clear the file cache for the argument state space.
* This deletes all files associated to this state space.
* @param stateSpace The state space.
*/
protected void clearCachedFiles(StateSpace stateSpace) {
Map<String,File> files = fileCache.remove(stateSpace);
if (files!=null) {
for (File file : files.values()) {
file.delete();
}
}
stateSpace.eAdapters().remove(cacheAdapter);
}
/**
* Export a state space as an AUT file.
* @param stateSpace State space.
* @param aut The AUT file.
* @throws IOException On I/O errors.
*/
protected File exportAsAUT(StateSpace stateSpace, IProgressMonitor monitor) throws IOException {
// Check the cached AUT file.
File cached = getCachedFile(stateSpace, AUT_FILE_EXPORT_KEY);
if (cached!=null) {
monitor.done();
return cached;
}
// Get the base name of the state space file:
String basename = stateSpace.eResource().getURI().trimFileExtension().lastSegment();
File aut = File.createTempFile(basename, ".aut");
URI uri = URI.createFileURI(aut.getAbsolutePath());
// Do the export:
AUTStateSpaceExporter exporter = new AUTStateSpaceExporter();
exporter.doExport(stateSpace, uri, null, monitor);
// We cache the file so that we don't have to generate it again:
addCachedFile(stateSpace, AUT_FILE_EXPORT_KEY, aut);
return aut;
}
protected File export(StateSpace stateSpace, StateSpaceExporter exporter, String parameters, String fileext, IProgressMonitor monitor) throws Exception {
// Temporary file.
String filename = stateSpace.eResource()!=null ?
stateSpace.eResource().getURI().trimFileExtension().lastSegment() : "statespace";
File tmp = createTempFile(filename, "." + fileext, null);
URI uri = URI.createFileURI(tmp.getAbsolutePath());
// Do the export:
exporter.setStateSpaceIndex(index);
exporter.doExport(stateSpace, uri, parameters, monitor);
return tmp;
}
/**
* Convert a file using a given command.
* @param input Input file.
* @param output Output file.
* @param command Command.
* @throws Exception On
*/
protected void convertFile(File input, File output, IProgressMonitor monitor, String... command) throws Exception {
String label = "Running " + command[0] + "...";
monitor.beginTask(label,1);
monitor.subTask(label);
command = Arrays.copyOf(command, command.length + 2);
command[command.length-2] = input.getAbsolutePath();
command[command.length-1] = output.getAbsolutePath();
// Increase stack size on Linux machines:
if (isLinux()) {
String mergedCommand = "";
for (String part : command) {
mergedCommand = mergedCommand + " " + part;
}
command = new String[] { "bash", "-c", "ulimit -s unlimited;" + mergedCommand };
}
// Print some debug info:
for (String part : command) {
System.out.print(part + " ");
}
System.out.println();
// Now start the process:
Process process = Runtime.getRuntime().exec(command);
int exit;
int wait = 50;
while (true) {
// Canceled?
if (monitor.isCanceled()) {
process.destroy();
exit = 0;
break;
}
// Poll:
try {
exit = process.exitValue();
break;
} catch (IllegalThreadStateException e) {}
// Wait:
try {
Thread.sleep(wait);
} catch (InterruptedException e) {}
// Increase the waiting time gradually:
wait = (int) (wait * 1.1);
}
// Correct exit code?
if (exit!=0) {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String out = "";
while (true) {
String line = reader.readLine();
if (line==null) break;
else out = out + "\n" + line;
}
throw new RuntimeException(command[0] + " returned exit code " + exit + ":\n" + out);
}
// Clean up.
process.destroy();
monitor.worked(1);
monitor.done();
}
/**
* Simple helper method for creating temporary files.
* @param content Content.
* @throws IOException I/O errors.
*/
protected File createTempFile(String prefix, String suffix, String content) throws IOException {
File temp = File.createTempFile(prefix, suffix);
if (content!=null) {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(temp)));
writer.write(content);
writer.close();
}
return temp;
}
/**
* Check whether the current platform is Windows.
* @return <code>true</code> if it is Windows.
*/
protected boolean isWindows() {
return Platform.OS_WIN32.equals(Platform.getOS());
}
/**
* Check whether the current platform is Linux.
* @return <code>true</code> if it is Linux.
*/
protected boolean isLinux() {
return Platform.OS_LINUX.equals(Platform.getOS());
}
}