Warning: Can't use blame annotator:
svn blame failed on trunk/src/scriptbuilder/structures/SimulationScript.java: ("Can't find a temporary directory: Internal error", 20014)

source: tmcsimulator-scriptbuilder/trunk/src/scriptbuilder/structures/SimulationScript.java @ 207

Revision 207, 14.3 KB checked in by jdalbey, 6 years ago (diff)

SimulationScript?.java modified to catch exception caused when external DTD not found during load xml and display helpful message to user. Fixes #240.

RevLine 
1package scriptbuilder.structures;
2
3import java.awt.Color;
4import java.io.BufferedWriter;
5import java.io.File;
6import java.io.FileWriter;
7import java.io.IOException;
8import java.util.ArrayList;
9import java.util.List;
10import java.util.Observable;
11import java.util.Vector;
12import javax.xml.parsers.SAXParserFactory;
13import scriptbuilder.structures.ScriptIncident.IncidentFocusedEvent;
14import scriptbuilder.structures.ScriptIncident.SliceChangedEvent;
15import scriptbuilder.structures.events.*;
16import scriptbuilder.structures.units.Unit;
17import java.nio.file.Path;
18import java.nio.file.Paths;
19import java.util.TreeMap;
20import javax.swing.JOptionPane;
21import static scriptbuilder.structures.XMLBuilder.prettyPrintXML;
22
23/**
24 * Representation of the script to be run by the TMC Simulator. Holds a list of
25 * incidents, which have start and end times and contain events.
26 *
27 * @author Greg Eddington <geddingt@calpoly.edu>
28 *
29 * @author Bryan McGuffin <bmcguffi@calpoly.edu>
30 * @author Sebastien Danthinne <sdanthin@calpoly.edu>
31 * @version 2017/06/22
32 */
33public class SimulationScript extends Observable implements I_XML_Writable
34{
35
36    /**
37     * Strings to show in the incident color combo box.  Last item should be black,
38     * which will be used if invalid color is provided to lookupColor().
39     */
40    public static final String[] colorNames = {"BLUE", "RED", "CYAN", "GREEN", 
41        "ORANGE", "MAGENTA", "YELLOW", "BLACK"};
42    /**
43     * Allowed color choices for incident display.
44     * These colors must match the items in colorNames.
45     */
46    public static final Color[] incidentColors = {Color.BLUE, Color.RED, Color.CYAN, 
47        Color.GREEN, Color.ORANGE, Color.MAGENTA, Color.YELLOW, Color.BLACK};
48    /**
49     * The file to which this script will be saved.
50     */
51    public File saveFile = null;
52
53    /**
54     * The name of this script.
55     */
56    public String title = "";
57
58    /**
59     * The incidents displayed by the GUI.
60     */
61    public List<ScriptIncident> incidents;
62
63    /**
64     * The units which participate in Unit events.
65     */
66    public List<Unit> units; 
67
68    //Somewhere in the code, something assumes that the list of incidents
69    //contains exactly 10 items. Until I can find and un-break that, this will do.
70    //todo: this incident fill count error
71    private final int INCIDENT_FILL_COUNT = 10;
72
73    /**
74     * Number of incidents currently displayed.
75     */
76    public int numberOfIncidents;
77
78    /**
79     * Script handler for parsing incoming XML files.
80     */
81    private MyScriptHandler sh;
82   
83    public final String audioDirectory = "Audio";
84
85   
86    public boolean saved;
87   
88    //TODO: Pretty much everything in this constructor is dummy data.
89    //Replace all of it.
90    /**
91     * Constructor. Backfill incident list with null objects, to be replaced
92     * later.
93     */
94    public SimulationScript()
95    {
96        sh = new MyScriptHandler(this);
97        incidents = new ArrayList<ScriptIncident>();
98        units = new ArrayList<Unit>();
99        numberOfIncidents = 0;
100        saved = true;
101
102        //Backfill with null incidents
103        for (int i = numberOfIncidents; i < INCIDENT_FILL_COUNT; i++)
104        {
105            incidents.add(null);
106        }
107    }
108   
109    /**
110     * checks and sees if this object has the unit passed.
111     * @param unitID
112     * @return does this SimulationScript have unitnum?
113     */
114    public boolean hasUnit(String unitID){
115        boolean indicator = false;
116        if(units.size()!=0){
117            for(Unit u : units){
118               if(unitID.equals(u.UnitNum)){
119                   indicator = true;
120               }
121            }
122        }
123           
124        return indicator;
125    }
126    /**
127     * creates a dummy unit that only has unit number
128     * @param unitNum
129     */
130    public void addDummyUnit(String unitNum){
131        Unit dummy = new Unit();
132        dummy.UnitNum = unitNum;
133        units.add(dummy);
134    }
135    /**
136     * Update the script's observers.
137     *
138     */
139    public void update()
140    {
141        // The script has changed, notify observers
142        //use to rewrite the save indicator
143        //System.out.println("Script changed");
144        setChanged();
145        notifyObservers(this);
146        saved = false;
147       
148       
149    }
150   
151
152    /**
153     * Tell this script's observers that there is a new slice event.
154     *
155     * @param e the slice focus event
156     */
157    public void broadcastEvent(SliceChangedEvent e)
158    {
159        // The slice focus has changed; pass the message
160        setChanged();
161        notifyObservers(e);
162    }
163
164    /**
165     * Tell this script's observers that there is a new slice event.
166     *
167     * @param e the incident focus event
168     */
169    public void broadcastEvent(IncidentFocusedEvent e)
170    {
171        // The slice focus has changed; pass the message
172        setChanged();
173        notifyObservers(e);
174    }
175
176    /**
177     * Load in an existing script from an XML file.
178     *
179     * @param infile the file containing the script
180     */
181    public void loadScriptFromFile(File infile)
182    {
183        try
184        {
185
186            SAXParserFactory.newInstance().newSAXParser().parse(infile, sh);
187
188            Vector<ScriptIncident> inc = sh.getIncidents();
189            units = sh.getUnits();
190            for (ScriptIncident sci : inc)
191            {
192                addIncident(sci);
193            }
194            saveFile = infile;
195
196        }
197        catch (Exception ex)
198        {
199            String errMsg = ex.getMessage();
200            if (ex.getMessage().endsWith(".dtd (No such file or directory)"))
201            {
202                errMsg += "\nThe required external DTD file must be placed in\n"
203                        + " the same folder as the xml file.";
204            }
205            JOptionPane.showMessageDialog(null, errMsg,
206                "Unable to load xml file",
207                JOptionPane.INFORMATION_MESSAGE);
208        }
209        this.update();
210    }
211   
212    /**
213     * Load in an existing list of units from an XML file.
214     * @param inStream the input stream for the file containing the units
215     */
216    public void loadUnitsFromFile(java.io.InputStream inStream){
217        try
218        {
219            SAXParserFactory.newInstance().newSAXParser().parse(inStream, sh);
220            units.addAll(sh.getUnits());
221        }
222        catch (Exception ex)
223        {
224            System.out.println("ERROR LOADING UNITS");
225            ex.printStackTrace();
226        }
227        this.update();
228    }
229
230    /**
231     * Add a new incident to the script.
232     *
233     * @param sci the incident to be added.
234     * @return true if there was enough room to add this incident.
235     */
236    public boolean addIncident(ScriptIncident sci)
237    {
238        if (numberOfIncidents < INCIDENT_FILL_COUNT)
239        {
240            incidents.set(numberOfIncidents++, sci);
241            return true;
242        }
243        return false;
244    }
245
246    /**
247     * Write this script, in proper XML format, to the file in question.
248     *
249     * @param f the destination savefile to be written.
250     */
251    public void saveScriptToFile(File f)
252    {
253        try
254        {
255            f.createNewFile();
256
257            BufferedWriter bw = new BufferedWriter(new FileWriter(f));
258            // convert to XML and remove newlines
259            String xmlOut = (this.toXML()).replace("\n",""); 
260            // pretty print and save to file
261            bw.write(prettyPrintXML(xmlOut));
262            bw.flush();
263            bw.close();
264        }
265        catch (Exception ex)
266        {
267            System.out.println("ERROR SAVING SCRIPT");
268            ex.printStackTrace();
269        }
270        try
271        {
272            createAudioDirectory(Paths.get(f.getCanonicalPath()).getParent());
273        }
274        catch(IOException ex)
275        {
276            System.err.println("there was a problem creating the audio directories.");
277        }
278       
279       
280        saved = true;
281    }
282   
283    /**
284     * Creates the proper audio directory using the I_AudioEvent types.
285     * @param path of audio directory root
286     */
287    private void createAudioDirectory(Path path){
288        String separator = System.getProperty("file.separator");
289        File f = new File(path.toString()+separator+audioDirectory);
290        if(!f.exists())
291        {
292            f.mkdir();
293            //scan through to see what is already there and if there is no existing audio directory, create it.
294        }
295        for(ScriptIncident i : incidents)
296        {
297            if(i!=null)
298            {
299                String name = ((Integer) i.number).toString();
300                File incidentFolder = new File(path.toString()+separator+audioDirectory+separator+name);
301                if(!incidentFolder.exists())
302                {
303                    //create an incidentfolder since one does not already exist
304                    incidentFolder.mkdir();
305                }
306
307               
308                for(TimeSlice slice : i.getSlices())
309                {
310                    for(I_ScriptEvent event : slice.events)
311                    {
312                        if(event instanceof I_AudioEvent)
313                        {
314                            //if the event is a chp radio event
315                            //then add the dummy file to the subdirectory created
316                            //CURRENTLY this ONLY is implemented for CHPRadioEvent, so it will need to be changed later.
317                            CHPRadioEvent radioEvent = (CHPRadioEvent) event;
318
319                            String output = radioEvent.toScriptFile();
320                            //optimally, this line should use the ID, but to account for files being read in, it needs to be the radiofile name
321                            System.out.println("Attempting to create file: "+ path.toString()+
322                                            separator+
323                                            audioDirectory+
324                                            separator+
325                                            name+
326                                            separator+
327                                            radioEvent.radioFile.replaceAll(".mp3","")+
328                                            ".txt");
329                            File newAudioScript = new File(
330                                    path.toString()+
331                                            separator+
332                                            audioDirectory+
333                                            separator+
334                                            name+
335                                            separator+
336                                            radioEvent.radioFile.replaceAll(".mp3","")+
337                                            ".txt");
338                           
339                            try
340                            {
341                                newAudioScript.createNewFile();
342                                BufferedWriter bw = new BufferedWriter(new FileWriter(newAudioScript));
343                                bw.write(output);
344                                bw.flush();
345                                bw.close();
346                            }catch(Exception e)
347                            {
348                                //to find the bug that triggers this Exception, we need to print out the lines that
349                                System.err.println("there was a problem creating your text files for: " + radioEvent.radioFile + "\n" + e.getMessage());
350                            }
351
352                        }
353                    }
354                }
355
356               
357            }
358
359           
360        }
361    }
362
363    @Override
364    public String toXML()
365    {
366        ArrayList<TimeSlice> slices = arrangeAllSlices();
367        String output = "";
368        output += XMLBuilder.openTag(ELEMENT.TMC_SCRIPT.tag + " title=\"" + this.title + "\"");
369
370        if (units.size() > 0)
371        {
372            output += XMLBuilder.openTag(ELEMENT.SCRIPT_DATA.tag);
373            for (Unit unit : units)
374            {
375                output += unit.toXML();
376            }
377            output += XMLBuilder.closeTag(ELEMENT.SCRIPT_DATA.tag);
378        }
379        for (TimeSlice slice : slices)
380        {
381            output += slice.toXML();
382        }
383        output += XMLBuilder.closeTag(ELEMENT.TMC_SCRIPT.tag);
384        return output;
385    }
386
387    /**
388     * Arranges all timeslices in this script in chronological order, then by
389     * incident number.
390     *
391     * @return a list of all timeslices in the simulation script
392     */
393    public ArrayList<TimeSlice> arrangeAllSlices()
394    {
395        ArrayList<TimeSlice> list = new ArrayList<TimeSlice>();
396        int length = absoluteLength();
397
398        for (int i = 0; i < length; i++)
399        {
400            for (ScriptIncident inc : incidents)
401            {
402
403                if (inc != null && inc.slices.get(i) != null)
404                {
405                    list.add(inc.slices.get(i));
406                }
407            }
408        }
409        return list;
410    }
411
412    /**
413     * Gets the total length of the simulation in seconds.
414     *
415     * @return
416     */
417    public int absoluteLength()
418    {
419        int length = 0;
420        for (ScriptIncident inc : incidents)
421        {
422            if (inc != null)
423            {
424                inc.updateLength();
425                int currentLength = inc.length + inc.offset;
426                if (currentLength > length)
427                {
428                    length = currentLength;
429                }
430            }
431        }
432        return length;
433    }
434
435    /**
436     * Counts the number of incidents currently running.
437     *
438     * @return the number of non-null incidents currently in the script. A
439     * number between 0 and INCIDENT_FILL_COUNT, inclusive.
440     */
441    public int incidentCount()
442    {
443        int count = 0;
444        for (ScriptIncident inc : incidents)
445        {
446            if (inc != null)
447            {
448                count++;
449            }
450        }
451        return count;
452    }
453   
454    /** Given a color, find its index in the incidentColors.
455     * @param color a java color
456     * @return the index of color in incidentColors, or last index if color isn't
457     * in incidentColors.  The last item in incidentColors should be black.
458     */
459    public static int lookupColor(Color color)
460    {
461        int idx = 0;
462        // search color array for target
463        while(idx < incidentColors.length && !incidentColors[idx].equals(color))
464        { 
465            idx++;
466        }
467        // if color not found, return index of last item.
468        if (idx == incidentColors.length) 
469        {
470            return idx-1;
471        }
472        else return idx;
473    }
474}
Note: See TracBrowser for help on using the repository browser.