diff --git a/src/main/java/net/berack/upo/valpre/Main.java b/src/main/java/net/berack/upo/valpre/Main.java index 64e0e3b..5144bb7 100644 --- a/src/main/java/net/berack/upo/valpre/Main.java +++ b/src/main/java/net/berack/upo/valpre/Main.java @@ -3,31 +3,83 @@ package net.berack.upo.valpre; import java.io.File; import java.net.URISyntaxException; import java.util.Arrays; +import java.util.HashMap; public class Main { - public static void main(String[] args) throws Exception { + public static void main(String[] args) { if (args.length == 0) - exit(); + exit("No program specified!"); - var subArgs = Arrays.copyOfRange(args, 1, args.length); - switch (args[0]) { - case "simulation": - var sim = new Simulation(subArgs); - sim.run(); - break; - case "plot": - var plot = new Plot(subArgs); - plot.show(); - break; - default: - exit(); + try { + var program = args[0]; + var subArgs = Arrays.copyOfRange(args, 1, args.length); + var param = Main.getParameters(program, subArgs); + switch (program) { + case "simulation" -> { + var net = param.get("net"); + var csv = param.get("csv"); + var runs = param.getOrDefault("runs", Integer::parseInt, 100); + var seed = param.getOrDefault("seed", Long::parseLong, 2007539552L); + var p = param.getOrDefault("p", Boolean::parseBoolean, false); + + var sim = new Simulation(net, seed, runs, p, csv); + sim.run(); + } + case "plot" -> { + var csv = param.get("csv"); + var plot = new Plot(csv); + plot.show(); + } + default -> exit("Invalid program!"); + } + } catch (Exception e) { + exit(e.getMessage()); } } - public static void exit() throws URISyntaxException { - var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI(); - var name = new File(uri).getName(); - System.out.println("Usage: java -jar " + name + ".jar [simulation|plot] [args]"); - System.exit(1); + /** + * Get the parameters from the arguments. + * + * @param program the program to run + * @param args the arguments to parse + * @return the parameters + */ + private static Parameters getParameters(String program, String[] args) { + var arguments = new HashMap(); + arguments.put("p", false); + arguments.put("seed", true); + arguments.put("runs", true); + arguments.put("net", true); + arguments.put("csv", true); + + var descriptions = new HashMap(); + descriptions.put("p", "Add this if you want the simulation to use threads (one each run)."); + descriptions.put("seed", "The seed of the simulation."); + descriptions.put("runs", "How many runs the simulator should run."); + descriptions.put("net", "The file net to use. Use example1.net or example2.net for the provided ones."); + + var csvDesc = switch (program) { + case "simulation" -> "The filename for saving every run statistics."; + case "plot" -> "The filename that contains the previous saved runs."; + default -> ""; + }; + descriptions.put("csv", csvDesc); + + return Parameters.getArgsOrHelper(args, "-", arguments, descriptions); + } + + /** + * Exit the program with an error message. + */ + public static void exit(String message) { + try { + var uri = Main.class.getProtectionDomain().getCodeSource().getLocation().toURI(); + var name = new File(uri).getName(); + System.out.println(message); + System.out.println("Usage: java -jar " + name + ".jar [simulation|plot] [args]"); + System.exit(1); + } catch (URISyntaxException e) { + e.printStackTrace(); + } } } \ No newline at end of file diff --git a/src/main/java/net/berack/upo/valpre/Parameters.java b/src/main/java/net/berack/upo/valpre/Parameters.java index 1c7b419..3f90d36 100644 --- a/src/main/java/net/berack/upo/valpre/Parameters.java +++ b/src/main/java/net/berack/upo/valpre/Parameters.java @@ -1,7 +1,11 @@ package net.berack.upo.valpre; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; /** * Class that helps with parsing the parameters passed as input in the console. @@ -9,6 +13,7 @@ import java.util.Map; public class Parameters { private final Map arguments; private final String prefix; + private Map parameters; /** * Constructs a new Parameters object with the specified prefix and arguments. @@ -27,6 +32,41 @@ public class Parameters { this.prefix = prefix; } + /** + * Get the size of the parameters. + * + * @return the size of the parameters + */ + public int size() { + return this.parameters.size(); + } + + /** + * Get the value of the argument passed as input. + * + * @param key the key of the argument + * @return the value of the argument + */ + public String get(String key) { + if (this.parameters == null) + return null; + return this.parameters.get(key); + } + + /** + * Get the value from the arguments or the default value if it is not present. + * + * @param key The key to get the value from. + * @param parse The function to parse the value. + * @param value The default value if the key is not present. + * @return The value from the arguments or the default value if it is not + * present. + */ + public T getOrDefault(String key, Function parse, T value) { + var arg = this.get(key); + return arg != null ? parse.apply(arg) : value; + } + /** * Return a string with the standard spaced enough * @@ -68,20 +108,21 @@ public class Parameters { } /** - * Parse the arguments passed and returns a map of Argument --> Value that can + * Parse the arguments passed and build a map of Argument --> Value that can * be used to retrieve the information. In the case that the arguments are not * in the correct format then an exception is thrown. + * To get the arguments use the {@link #get(String)} method. * * @param args the arguments in input * @throws IllegalArgumentException if the arguments are not formatted correctly - * or if there is an unknown argument - * @return a map of the values + * or if there is an unknown argument or there + * are not arguments in the input */ - public Map parse(String[] args) { - var result = new HashMap(); + public void parse(String[] args) { if (args == null || args.length == 0) - return result; + throw new IllegalArgumentException("No arguments passed"); + var result = new HashMap(); for (var i = 0; i < args.length; i += 1) { var current = args[i]; var next = i + 1 < args.length ? args[i + 1] : null; @@ -91,7 +132,7 @@ public class Parameters { i += 1; } - return result; + this.parameters = result; } /** @@ -121,7 +162,7 @@ public class Parameters { result.put(current, ""); if (finalSize != result.size()) - throw new IllegalArgumentException("Argument unknown"); + throw new IllegalArgumentException("Unknown argument [" + current + "]"); return false; } @@ -129,6 +170,7 @@ public class Parameters { * Parse the arguments passed and returns a map of Argument --> Value that can * be used to retrieve the information. In the case that the arguments are not * in the correct format then an exception is thrown and the helper is printed. + * If the arguments passed are 0 then the helper is printed. * * @param args the arguments in input * @param prefix the prefix to be used @@ -139,12 +181,13 @@ public class Parameters { * or if there is an unknown argument * @return a map of the values */ - public static Map getArgsOrHelper(String[] args, String prefix, Map arguments, + public static Parameters getArgsOrHelper(String[] args, String prefix, Map arguments, Map descriptions) { var param = new Parameters(prefix, arguments); try { - return param.parse(args); + param.parse(args); + return param; } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); System.out.println(param.helper(descriptions)); @@ -157,10 +200,17 @@ public class Parameters { * * @param file the file to get * @return the file or the example file + * @throws FileNotFoundException if the file is not found */ - public static String getFileOrExample(String file) { - if (file.startsWith("example")) - file = Main.class.getClassLoader().getResource(file).getPath(); - return file; + public static InputStream getFileOrExample(String file) throws FileNotFoundException { + if (file == null) + return null; + + if (file.startsWith("example")) { + var resource = Parameters.class.getClassLoader().getResourceAsStream(file); + if (resource != null) + return resource; + } + return new FileInputStream(file); } } diff --git a/src/main/java/net/berack/upo/valpre/Plot.java b/src/main/java/net/berack/upo/valpre/Plot.java index 6e06459..867e101 100644 --- a/src/main/java/net/berack/upo/valpre/Plot.java +++ b/src/main/java/net/berack/upo/valpre/Plot.java @@ -3,8 +3,6 @@ package net.berack.upo.valpre; import java.awt.BorderLayout; import java.awt.Font; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; @@ -42,13 +40,13 @@ public class Plot { * @param args the arguments to create the plot * @throws IOException if anything happens while reading the file */ - public Plot(String[] args) throws IOException { - var arguments = Plot.parseParameters(args); - var file = Parameters.getFileOrExample(arguments.get("csv")); - if (file == null) - throw new IllegalArgumentException("CSV file needed! Use -csv "); + public Plot(String csv) throws IOException { + var stream = Parameters.getFileOrExample(csv); + if (stream == null) + throw new IllegalArgumentException("CSV file needed!"); + var results = CsvResult.loadResults(stream); + stream.close(); - var results = new CsvResult(file).loadResults(); this.summary = new ResultSummary(results); var nodes = this.summary.getNodes().toArray(new String[0]); @@ -157,22 +155,6 @@ public class Plot { } } - /** - * Parse the arguments to get the CSV file. - * - * @param args the arguments to parse - * @return a map with the arguments - */ - private static Map parseParameters(String[] args) { - var arguments = new HashMap(); - arguments.put("csv", true); - - var descriptions = new HashMap(); - descriptions.put("csv", "The filename that contains the previous saved runs."); - - return Parameters.getArgsOrHelper(args, "-", arguments, descriptions); - } - /** * This class is used to create a panel with a name and a value. * The name is on the left and the value is on the right. diff --git a/src/main/java/net/berack/upo/valpre/Simulation.java b/src/main/java/net/berack/upo/valpre/Simulation.java index bd7b8f2..7afb444 100644 --- a/src/main/java/net/berack/upo/valpre/Simulation.java +++ b/src/main/java/net/berack/upo/valpre/Simulation.java @@ -1,11 +1,8 @@ package net.berack.upo.valpre; +import java.io.FileNotFoundException; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ExecutionException; -import java.util.function.Function; - import com.esotericsoftware.kryo.KryoException; import net.berack.upo.valpre.sim.Net; @@ -21,24 +18,32 @@ public class Simulation { public final int runs; public final long seed; public final boolean parallel; - public final String file; + public final Net net; /** * Create a new simulation with the given arguments. * * @param args The arguments for the simulation. + * @throws IOException if the file is has a problem */ - public Simulation(String[] args) { - // Evantually change the parameters - var arguments = Simulation.parseParameters(args); - this.runs = Simulation.getFromArguments(arguments, "runs", Integer::parseInt, 100); - this.seed = Simulation.getFromArguments(arguments, "seed", Long::parseLong, 2007539552L); - this.csv = arguments.getOrDefault("csv", null); - this.parallel = arguments.containsKey("p"); + public Simulation(String netFile, long seed, int runs, boolean parallel, String csv) throws IOException { + if (runs <= 0) + throw new IllegalArgumentException("Runs must be greater than 0!"); - this.file = Parameters.getFileOrExample(arguments.get("net")); - if (this.file == null) - throw new IllegalArgumentException("Net file needed! Use -net "); + this.runs = runs; + this.seed = seed; + this.csv = csv; + this.parallel = parallel; + + try { + var file = Parameters.getFileOrExample(netFile); + this.net = Net.load(file); + file.close(); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("Net file needed!"); + } catch (KryoException e) { + throw new IllegalArgumentException("Net file is not valid or corrupted!"); + } } /** @@ -51,9 +56,8 @@ public class Simulation { * @throws IOException If the simulation fails. */ public void run() throws InterruptedException, ExecutionException, KryoException, IOException { - var net = Net.load(this.file); var nano = System.nanoTime(); - var sim = new SimulationMultiple(net); + var sim = new SimulationMultiple(this.net); var summary = this.parallel ? sim.runParallel(this.seed, this.runs) : sim.run(this.seed, this.runs); nano = System.nanoTime() - nano; @@ -65,45 +69,4 @@ public class Simulation { System.out.println("Data saved to " + this.csv); } } - - /** - * Get the value from the arguments or the default value if it is not present. - * - * @param args The arguments for the simulation. - * @param key The key to get the value from. - * @param parse The function to parse the value. - * @param value The default value if the key is not present. - * @return The value from the arguments or the default value if it is not - * present. - */ - private static T getFromArguments(Map args, String key, Function parse, T value) { - if (args.containsKey(key)) - return parse.apply(args.get(key)); - return value; - } - - /** - * Parse the arguments for the simulation. - * - * @param args The arguments for the simulation. - * @return The parsed arguments for the simulation. - */ - private static Map parseParameters(String[] args) { - var arguments = new HashMap(); - arguments.put("p", false); - arguments.put("seed", true); - arguments.put("runs", true); - arguments.put("net", true); - arguments.put("csv", true); - - var descriptions = new HashMap(); - descriptions.put("p", "Add this if you want the simulation to use threads (one each run)."); - descriptions.put("seed", "The seed of the simulation."); - descriptions.put("runs", "How many runs the simulator should run."); - descriptions.put("net", - "The net to use. It should be a file. Use example1.net or example2.net for the provided ones."); - descriptions.put("csv", "The filename for saving every run statistics."); - - return Parameters.getArgsOrHelper(args, "-", arguments, descriptions); - } } diff --git a/src/main/java/net/berack/upo/valpre/sim/Net.java b/src/main/java/net/berack/upo/valpre/sim/Net.java index 8bf8842..89ac3d8 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Net.java +++ b/src/main/java/net/berack/upo/valpre/sim/Net.java @@ -3,6 +3,8 @@ package net.berack.upo.valpre.sim; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -250,15 +252,29 @@ public final class Net { * * @param file the file to load * @return a new Net object - * @throws KryoException if the file saved is not a net - * @throws FileNotFoundException if the file is not found + * @throws KryoException if the file saved is not a net + * @throws IOException if the file is not found */ - public static Net load(String file) throws KryoException, FileNotFoundException { + public static Net load(String file) throws KryoException, IOException { + try (var stream = new FileInputStream(file)) { + return Net.load(stream); + } + } + + /** + * Load the net from the stream passed as input. + * The net will be the same as the one saved. + * + * @param stream the input stream to read + * @return a new Net object + * @throws KryoException if the file saved is not a net + */ + public static Net load(InputStream stream) throws KryoException { var kryo = new Kryo(); kryo.setRegistrationRequired(false); kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); - try (var in = new Input(new FileInputStream(file))) { + try (var in = new Input(stream)) { return (Net) kryo.readClassAndObject(in); } } diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/CsvResult.java b/src/main/java/net/berack/upo/valpre/sim/stats/CsvResult.java index 1a15f33..73c888e 100644 --- a/src/main/java/net/berack/upo/valpre/sim/stats/CsvResult.java +++ b/src/main/java/net/berack/upo/valpre/sim/stats/CsvResult.java @@ -1,8 +1,9 @@ package net.berack.upo.valpre.sim.stats; -import java.io.File; +import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -59,8 +60,20 @@ public class CsvResult { * @throws IOException if anything happens while reading the file */ public Result[] loadResults() throws IOException { + try (var stream = new FileInputStream(this.file)) { + return CsvResult.loadResults(stream); + } + } + + /** + * Load the results from the CSV stream. + * + * @param input the input stream to read + * @return the results loaded from the stream + */ + public static Result[] loadResults(InputStream input) { var results = new ArrayList(); - try (var scan = new Scanner(new File(this.file))) { + try (var scan = new Scanner(input)) { var _ = scan.nextLine(); var nodes = new HashMap();