package jamel; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.prefs.Preferences; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import jamel.util.NotYetImplementedException; import jamel.util.Simulation; /** * The main class for Jamel. */ public class Jamel { /** * Provides a simple mechanism for the user to choose a scenario. */ private static class Chooser implements Runnable { /** * The selected file. */ private File file = null; /** * Returns the selected file. * * @return the selected file. */ public File getFile() { return file; } /** * Pops up an "Open File" file chooser dialog. */ @Override public void run() { final Preferences prefs = Preferences.userRoot(); final String PREF_SCENARIO_PATH = version + ".scenario.path"; final String defaultFolder = "scenarios"; final String path = prefs.get(PREF_SCENARIO_PATH, defaultFolder); final JFileChooser fc = new JFileChooser() { { this.setFileFilter(new FileNameExtensionFilter("XML files", "xml")); } }; File dir = new File(path); if (!dir.exists()) { dir = new File(defaultFolder); } fc.setDialogTitle("Open Scenario"); fc.setCurrentDirectory(dir); final int returnVal = fc.showOpenDialog(null); if (returnVal == JFileChooser.APPROVE_OPTION) { file = fc.getSelectedFile(); final File parent = file.getParentFile(); prefs.put(PREF_SCENARIO_PATH, parent.getPath()); } else { file = null; } } } /** The user preferences. */ /** This version of Jamel. */ final private static String version = "jamel-20170501"; /** * Creates and returns a new simulation. * * @param element * an XML element that contains the description of the new * simulation. * @param file * the file of the scenario. * @return a new simulation. */ private static Simulation newSimulation(final Element element, final File file) { if (file == null) { throw new IllegalArgumentException("Path is null"); } final Simulation simulation; if (!element.getNodeName().equals("simulation")) { throw new RuntimeException("Bad element: " + element.getNodeName()); } final Node simulationClassNameNode = element.getElementsByTagName("simulationClassName").item(0); /*final String simulationClassName = element.getAttribute("className"); if (simulationClassName.isEmpty()) { throw new RuntimeException("Attribute \"className\" is missing or empty."); }*/if (simulationClassNameNode==null) { throw new RuntimeException("Missing node: \"simulationClassName\"."); } try { simulation = (Simulation) Class.forName(simulationClassNameNode.getTextContent().trim(), false, ClassLoader.getSystemClassLoader()) .getConstructor(Element.class, File.class).newInstance(element, file); } catch (Exception e) { throw new RuntimeException("Something went wrong while creating the simulation.", e); } return simulation; } /** * Repeats several simulations. * * @param element * an element that contains the description of the simulations to * be repeated. * @param parentFile * the parent file. */ private static void repeat(final Element element, final File parentFile) { // Sets the name of the scenario file. if (element.getAttribute("src").isEmpty()) { throw new RuntimeException("Simulation attribute src is empty or missing."); } final String fileName = parentFile.getPath() + "/" + element.getAttribute("src"); // Reading the scenario file. String scenario; try (final BufferedReader reader = new BufferedReader(new FileReader(fileName));) { final StringBuilder stringBuilder = new StringBuilder(); final String ls = System.getProperty("line.separator"); String line = null; while ((line = reader.readLine()) != null) { stringBuilder.append(line); stringBuilder.append(ls); } scenario = stringBuilder.toString(); reader.close(); } catch (IOException e) { throw new RuntimeException("Something went wrong while reading this file: " + fileName, e); } // Replaces some strings in the scenario final NodeList replaceNodeList = element.getElementsByTagName("replace"); for (int i = 0; i < replaceNodeList.getLength(); i++) { final Element item = (Element) replaceNodeList.item(i); final String regex = item.getAttribute("regex"); final String replacement = item.getAttribute("replacement"); scenario = scenario.replaceAll(regex, replacement); } // Sets the number of replications final int replications; if (element.getAttribute("repeat").isEmpty()) { throw new RuntimeException("Repeat attribute is missing or empty."); } try { replications = Integer.parseInt(element.getAttribute("repeat")); } catch (@SuppressWarnings("unused") java.lang.NumberFormatException e) { throw new RuntimeException("Repeat attribute is not a number: " + element.getAttribute("repeat")); } // Gets the variables. final Map<String, String[]> variables = new LinkedHashMap<>(); final NodeList nodeList = element.getElementsByTagName("variable"); for (int i = 0; i < nodeList.getLength(); i++) { final Element item = (Element) nodeList.item(i); if (item.getAttribute("key").isEmpty()) { throw new RuntimeException("key attribute is missing or empty."); } final String key = item.getAttribute("key"); if (item.getAttribute("values").isEmpty()) { throw new RuntimeException("values attribute is missing or empty."); } final String[] values = item.getAttribute("values").split(","); if (values.length != replications) { Jamel.println(); Jamel.println("key: " + key); Jamel.println("values: " + item.getAttribute("values")); Jamel.println("values.lenght() is: " + values.length); Jamel.println("but expected was: " + replications); Jamel.println(); throw new RuntimeException("Error while parsing the variables. See log file for more details."); } variables.put(key, values); } // Repeats the simulation for (int i = 0; i < replications; i++) { String newScenario = scenario; // Changes the variables for (String key : variables.keySet()) { newScenario = newScenario.replaceAll(key, variables.get(key)[i]); } final Element elem2; try { elem2 = DocumentBuilderFactory.newInstance().newDocumentBuilder() .parse(new InputSource(new StringReader(newScenario))).getDocumentElement(); } catch (SAXException | IOException | ParserConfigurationException e) { Jamel.println(); Jamel.println("fileName", fileName); Jamel.println("replication", i); Jamel.println(); throw new RuntimeException("Something went wrong while creating the XML document.", e); } simulate(elem2, new File(fileName)); } } /** * Runs Jamel. */ private static void run() { final Chooser scenarioChooser = new Chooser(); try { SwingUtilities.invokeAndWait(scenarioChooser); } catch (InvocationTargetException | InterruptedException e) { throw new RuntimeException("Something went wrong while choosing the scenario file.", e); } final File file = scenarioChooser.getFile(); if (file != null) { Jamel.println("run " + file.getPath()); final Document document; try { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file); } catch (final Exception e) { throw new RuntimeException("Something went wrong while reading the file \"" + file.getName() + "\"", e); } final Element root = document.getDocumentElement(); if (root.getTagName().equals("simulation")) { // C'est une simulation, on l'exécute directement. simulate(root, file); } else if (root.getTagName().equals("multi-simulation")) { // C'est une collection de simulations, // on les exécute les unes après les autres final NodeList nodeList = root.getElementsByTagName("simulation"); for (int index = 0; index < nodeList.getLength(); index++) { final Element element = (Element) nodeList.item(index); repeat(element, file.getParentFile()); } } else { // C'est n'importe quoi, fin. throw new RuntimeException("Bad root element: " + root.getTagName()); } } } /** * Creates and runs a new simulation. * * @param scenario * an element that contains the description of the simulation. * @param file * the file. */ private static void simulate(final Element scenario, final File file) { final Simulation simulation = newSimulation(scenario, file); try { simulation.run(); } catch (RuntimeException e) { e.printStackTrace(); } } /** * Displays an error message; * * @param title * the title. * @param message * the message. */ public static void errorMessage(final String title, final String message) { JOptionPane.showMessageDialog(null, "<html>Jamel said:<br>\"" + message + "\"<br>See the console for more details.</html>", title, JOptionPane.ERROR_MESSAGE); } /** * The main method for Jamel. * * @param args * unused. */ public static void main(String[] args) { final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MMM d HH:mm:ss", Locale.US); Jamel.println(version); Jamel.println("Start", simpleDateFormat.format(new Date())); Jamel.println(); try { run(); } catch (RuntimeException e) { e.printStackTrace(); final String message; if (e.getCause() != null && e.getCause().getMessage() != null) { message = e.getMessage() + "<br>" + e.getCause().getMessage(); } else { message = e.getMessage(); } errorMessage("Runtime Error", message); } Jamel.println(); Jamel.println("End", simpleDateFormat.format(new Date())); Jamel.println(); } /** * Throws a new <code>NotYetImplementedException</code>. */ public static void notYetImplemented() { throw new NotYetImplementedException(); } /** * A short cut for <code>System.out.println()</code>. */ public static void println() { System.out.println(); } /** * Prints the specified numbers into the "standard" output stream. Numbers * are printed on the same line and are separated by commas. * * @param numbers * the numbers to be printed. */ public static void println(Number... numbers) { for (int i = 0; i < numbers.length; i++) { System.out.print(numbers[i]); if (i < numbers.length - 1) { System.out.print(", "); } } System.out.println(); } /** * Prints several objects. * * @param objects * The <code>Objects</code> to be printed. */ public static void println(Object... objects) { for (int i = 0; i < objects.length; i++) { final String string; if (objects[i] == null) { string = "null"; } else { string = objects[i].toString(); } System.out.print(string); if (i < objects.length - 1) { System.out.print(", "); } } System.out.println(); } /** * Prints the specified strings into the "standard" output stream. Strings * are printed on the same line and are separated by commas. * * @param strings * the strings to be printed. */ public static void println(String... strings) { for (int i = 0; i < strings.length; i++) { System.out.print(strings[i]); if (i < strings.length - 1) { System.out.print(", "); } } System.out.println(); } } // ***