package scriptbuilder.structures; import java.awt.Color; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Observable; import java.util.Vector; import javax.xml.parsers.SAXParserFactory; import scriptbuilder.structures.ScriptIncident.IncidentFocusedEvent; import scriptbuilder.structures.ScriptIncident.SliceChangedEvent; import scriptbuilder.structures.events.*; import scriptbuilder.structures.units.Unit; import java.nio.file.Path; import java.nio.file.Paths; import java.util.TreeMap; /** * Representation of the script to be run by the TMC Simulator. Holds a list of * incidents, which have start and end times and contain events. * * @author Greg Eddington * * @author Bryan McGuffin * @author Sebastien Danthinne * @version 2017/06/22 */ public class SimulationScript extends Observable implements I_XML_Writable { /** * All default options for GUI colorings of incidents. */ public static final Color[] incidentColors = { Color.BLACK, Color.BLUE, Color.RED, new Color(0x38, 0x5E, 0x0F), new Color(128, 0, 128), Color.MAGENTA, new Color(0x23, 0x6B, 0x8E), Color.ORANGE, new Color(0x60, 0x33, 0x11), Color.GRAY }; /** * The file to which this script will be saved. */ public File saveFile = null; /** * The name of this script. */ public String title = ""; /** * The incidents displayed by the GUI. */ public List incidents; /** * The units which participate in Unit events. */ public List units; //Somewhere in the code, something assumes that the list of incidents //contains exactly 10 items. Until I can find and un-break that, this will do. //todo: this incident fill count error private final int INCIDENT_FILL_COUNT = 10; /** * Number of incidents currently displayed. */ public int numberOfIncidents; /** * Script handler for parsing incoming XML files. */ private MyScriptHandler sh; public boolean saved; //TODO: Pretty much everything in this constructor is dummy data. //Replace all of it. /** * Constructor. Backfill incident list with null objects, to be replaced * later. */ public SimulationScript() { sh = new MyScriptHandler(this); incidents = new ArrayList(); units = new ArrayList(); numberOfIncidents = 0; saved = true; //Backfill with null incidents for (int i = numberOfIncidents; i < INCIDENT_FILL_COUNT; i++) { incidents.add(null); } } /** * checks and sees if this object has the unit passed. * @param unitID * @return does this SimulationScript have unitnum? */ public boolean hasUnit(String unitID){ boolean indicator = false; if(units.size()!=0){ for(Unit u : units){ if(unitID.equals(u.UnitNum)){ indicator = true; } } } return indicator; } /** * creates a dummy unit that only has unit number * @param unitNum */ public void addDummyUnit(String unitNum){ Unit dummy = new Unit(); dummy.UnitNum = unitNum; units.add(dummy); } /** * Update the script's observers. * */ public void update() { // The script has changed, notify observers //use to rewrite the save indicator //System.out.println("Script changed"); setChanged(); notifyObservers(this); saved = false; } /** * Tell this script's observers that there is a new slice event. * * @param e the slice focus event */ public void broadcastEvent(SliceChangedEvent e) { // The slice focus has changed; pass the message setChanged(); notifyObservers(e); } /** * Tell this script's observers that there is a new slice event. * * @param e the incident focus event */ public void broadcastEvent(IncidentFocusedEvent e) { // The slice focus has changed; pass the message setChanged(); notifyObservers(e); } /** * Load in an existing script from an XML file. * * @param f the file containing the script */ public void loadScriptFromFile(File f) { try { SAXParserFactory.newInstance().newSAXParser().parse(f, sh); Vector inc = sh.getIncidents(); units = sh.getUnits(); for (ScriptIncident sci : inc) { addIncident(sci); } } catch (Exception ex) { System.out.println("ERROR LOADING SCRIPT"); ex.printStackTrace(); } System.out.println("H"); for(Unit testUnit : units){ System.out.println(testUnit.toXML()); } this.update(); } /** * Load in an existing list of units from an XML file. * @param f the file containing the units */ public void loadUnitsFromFile(File f){ try { SAXParserFactory.newInstance().newSAXParser().parse(f, sh); units.addAll(sh.getUnits()); } catch (Exception ex) { System.out.println("ERROR LOADING UNITS"); ex.printStackTrace(); } this.update(); } /** * Add a new incident to the script. * * @param sci the incident to be added. * @return true if there was enough room to add this incident. */ public boolean addIncident(ScriptIncident sci) { if (numberOfIncidents < INCIDENT_FILL_COUNT) { incidents.set(numberOfIncidents++, sci); return true; } return false; } /** * Write this script, in proper XML format, to the file in question. * * @param f the destination savefile to be written. */ public void saveScriptToFile(File f) { try { f.createNewFile(); BufferedWriter bw = new BufferedWriter(new FileWriter(f)); bw.write(this.toXML()); bw.flush(); bw.close(); } catch (Exception ex) { System.out.println("ERROR SAVING SCRIPT"); ex.printStackTrace(); } try { createAudioDirectory(Paths.get(f.getCanonicalPath()).getParent()); } catch(IOException ex) { System.err.println("there was a problem creating the audio directories."); } saved = true; } private void createAudioDirectory(Path path){ File f = new File(path.toString()+"/audio"); if(!f.exists()) { f.mkdir(); //scan through to see what is already there and then make changes accordingly. } for(ScriptIncident i : incidents) { if(i!=null) { String name = ((Integer) i.number).toString(); File incidentFolder = new File(path.toString()+"/audio/"+name); if(incidentFolder.exists()) { //check to see if there are already files for the given incidents //not sure how the ordering of the files should be. //check to see if folder needs filling }else { //create an incidentfolder since one does not already exist incidentFolder.mkdir(); int fileCount = 1; for(TimeSlice slice : i.getSlices()) { for(I_ScriptEvent event : slice.events) { if(event.getScriptEventType().equals(ScriptEvent.ScriptEventType.CHP_RADIO_EVENT)) { //if the event is a chp radio event //then add the dummy file to the subdirectory created CHPRadioEvent radioEvent = (CHPRadioEvent) event; String output = radioEvent.toScriptFile(); File newAudioScript = new File(path.toString()+"/audio/"+name+"/"+name+fileCount+".txt"); fileCount++; try { newAudioScript.createNewFile(); BufferedWriter bw = new BufferedWriter(new FileWriter(newAudioScript)); bw.write(output); bw.flush(); bw.close(); }catch(Exception e) { System.err.println("there was a problem creating your text files"); } } } } } } } } @Override public String toXML() { ArrayList slices = arrangeAllSlices(); String output = "\n"; output += XMLWriter.internalDTD(); output += XMLWriter.openTag(ELEMENT.TMC_SCRIPT.tag + " title=\"" + this.title + "\""); if (units.size() > 0) { output += XMLWriter.openTag(ELEMENT.SCRIPT_DATA.tag); for (Unit unit : units) { output += unit.toXML(); } output += XMLWriter.closeTag(ELEMENT.SCRIPT_DATA.tag); } for (TimeSlice slice : slices) { output += slice.toXML(); } output += XMLWriter.closeTag(ELEMENT.TMC_SCRIPT.tag); return output; } /** * Arranges all timeslices in this script in chronological order, then by * incident number. * * @return a list of all timeslices in the simulation script */ public ArrayList arrangeAllSlices() { ArrayList list = new ArrayList(); int length = absoluteLength(); for (int i = 0; i < length; i++) { for (ScriptIncident inc : incidents) { if (inc != null && inc.slices.get(i) != null) { list.add(inc.slices.get(i)); } } } return list; } /** * Gets the total length of the simulation in seconds. * * @return */ public int absoluteLength() { int length = 0; for (ScriptIncident inc : incidents) { if (inc != null) { inc.updateLength(); int currentLength = inc.length + inc.offset; if (currentLength > length) { length = currentLength; } } } return length; } /** * Counts the number of incidents currently running. * * @return the number of non-null incidents currently in the script. A * number between 0 and INCIDENT_FILL_COUNT, inclusive. */ public int incidentCount() { int count = 0; for (ScriptIncident inc : incidents) { if (inc != null) { count++; } } return count; } }