package main;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.CompletionService;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import gui.views.MainWindow;
import hexcapture.HexPipeCompleter;
import hextostring.HexProcessor;
import hextostring.history.History;
import main.options.Options;
import main.options.annotations.CommandLineArgument;
import main.utils.IOUtils;
import main.utils.ReflectionUtils;
/**
* Standard main class of the program.
*
* @author Maxime PIA
*/
public class Main {
private static void attachSerializeOnClose(final MainOptions opts,
final HexPipeCompleter hpc) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
saveOptions(opts);
hpc.closeHandle();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
});
}
private static void saveOptions(MainOptions opts) throws IOException {
FileOutputStream fos = new FileOutputStream(
IOUtils.getFileInAppdataDirectory(Options.SERIALIAZATION_FILENAME)
);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(opts);
oos.close();
}
private static MainOptions restoreOptions() throws IOException,
ClassNotFoundException {
FileInputStream fin = new FileInputStream(
IOUtils.getFileInAppdataDirectory(Options.SERIALIAZATION_FILENAME)
);
ObjectInputStream ois = new ObjectInputStream (fin);
MainOptions res = (MainOptions) ois.readObject();
ois.close();
return res;
}
// The behavior of this method is as follows:
// - If a new options has been introduces in opts, a new instance of this
// option class is created using a default constructor.
// - If a CommandLineArgument annotated field is null due to version
// inconsistencies, the default value for this field is fetched.
// If no default value can be found (which should never happen),
// the containing object is replaced by a new instance of its class.
//
// If this method throws an exception, a new instance of MainOptions should
// be used instead of opts.
private static boolean correctVersionIncompatibilities(MainOptions opts)
throws IllegalArgumentException, IllegalAccessException,
InstantiationException, SecurityException {
boolean incompatibilityDetected = false;
Field[] optFields = opts.getClass().getDeclaredFields();
for (Field optField : optFields) {
if (!Options.class.equals(optField.getType().getSuperclass())) {
continue;
}
optField.setAccessible(true);
if (optField.get(opts) == null) {
// handling new option types
incompatibilityDetected = true;
optField.set(opts, optField.getClass().newInstance());
} else {
// handling new configurable fields
List<Field> configurableFields =
ReflectionUtils.getAnnotatedFields(
optField.getType(),
CommandLineArgument.class
);
Options subOpts = (Options) optField.get(opts);
for (Field configurableField : configurableFields) {
configurableField.setAccessible(true);
if (configurableField.get(subOpts) == null) {
incompatibilityDetected = true;
try {
configurableField.set(
subOpts,
subOpts.getFieldDefaultValue(configurableField)
);
} catch (NoSuchFieldException e) {
optField
.set(opts, optField.getClass().newInstance());
continue;
}
}
}
}
}
opts.updateSubOptionsSet();
return incompatibilityDetected;
}
/**
* Starts a conversion session and displays the GUI.
*
* @param args
* See {@link main.MainOptions}
*/
public static void main(String[] args) {
try {
final History history = new History();
HexPipeCompleter hpc = new HexPipeCompleter();
MainOptions opts;
boolean serializationWarning = false;
try {
opts = restoreOptions();
try {
serializationWarning =
correctVersionIncompatibilities(opts);
} catch (IllegalArgumentException | IllegalAccessException
| InstantiationException | SecurityException e) {
e.printStackTrace();
serializationWarning = true;
opts = new MainOptions();
}
opts.parseArgs(args);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
serializationWarning = true;
opts = new MainOptions(args);
}
attachSerializeOnClose(opts, hpc);
hpc.updateConfig(opts.getHexOptions());
HexProcessor hp =
new HexProcessor(opts.getConvertOptions(), history);
MainWindow window =
new MainWindow(hp, opts, history, serializationWarning);
final InputInterpreter ii =
new InputInterpreter(opts, System.out, hp, window);
CompletionService<Boolean> ecs =
new ExecutorCompletionService<Boolean>(new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
});
ecs.submit(new Runnable() {
@Override
public void run() {
ii.start();
}
}, true);
if (ecs.take().get() != null) {
window.dispose();
}
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}