1
2
3
4
5
6
7 package nl.tudelft.simulation.xml.dsol;
8
9 import java.io.IOException;
10 import java.net.MalformedURLException;
11 import java.net.URL;
12 import java.net.URLClassLoader;
13 import java.text.DateFormat;
14 import java.util.ArrayList;
15 import java.util.Calendar;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Properties;
21
22 import javax.naming.Context;
23 import javax.naming.InitialContext;
24 import javax.naming.NamingException;
25
26 import nl.tudelft.simulation.dsol.ModelInterface;
27 import nl.tudelft.simulation.dsol.experiment.Experiment;
28 import nl.tudelft.simulation.dsol.experiment.ExperimentalFrame;
29 import nl.tudelft.simulation.dsol.experiment.Replication;
30 import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
31 import nl.tudelft.simulation.dsol.experiment.Treatment;
32 import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
33 import nl.tudelft.simulation.jstats.streams.MersenneTwister;
34 import nl.tudelft.simulation.jstats.streams.StreamInterface;
35 import nl.tudelft.simulation.language.io.URLResource;
36 import nl.tudelft.simulation.language.reflection.ClassUtil;
37 import nl.tudelft.simulation.logger.Logger;
38
39 import org.jdom.Element;
40 import org.jdom.JDOMException;
41 import org.jdom.input.SAXBuilder;
42
43 /***
44 * The ExperimentParser parses xml-based experiments into their java objects
45 * <br>
46 * (c) copyright 2002-2005 <a href="http://www.simulation.tudelft.nl">Delft
47 * University of Technology </a>, the Netherlands. <br>
48 * See for project information <a href="http://www.simulation.tudelft.nl">
49 * www.simulation.tudelft.nl </a> <br>
50 * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser
51 * General Public License (LGPL) </a>, no warranty.
52 *
53 * @version 2.0 21.09.2003 <br>
54 * @author <a href="http://www.peter-jacobs.com/index.htm">Peter Jacobs </a>
55 */
56 public class ExperimentParser
57 {
58 /*** builder the xerces parser with validation turned on */
59 private static SAXBuilder builder = new SAXBuilder(
60 "org.apache.xerces.parsers.SAXParser", true);
61 static
62 {
63
64 builder.setFeature("http://xml.org/sax/features/validation", true);
65 builder.setFeature("http://apache.org/xml/features/validation/schema",
66 true);
67
68 String xsd = URLResource.getResource("/xsd/experiment.xsd")
69 .toExternalForm();
70 builder
71 .setProperty(
72 "http://apache.org/xml/properties/schema/external-schemaLocation",
73 "http://www.simulation.tudelft.nl " + xsd);
74 }
75
76 /***
77 * constructs a new ExperimentParser This is a Utility Class.
78 */
79 protected ExperimentParser()
80 {
81
82 }
83
84 /***
85 * parses an experimentalFrame xml-file.
86 *
87 * @param input the inputstream
88 * @return ExperimentalFrame the experimentalFrame
89 * @throws IOException whenever parsing fails
90 */
91 public static ExperimentalFrame parseExperimentalFrame(final URL input)
92 throws IOException
93 {
94 try
95 {
96 return ExperimentParser.parseExperimentalFrame(
97 new InitialContext(), input);
98 } catch (NamingException exception)
99 {
100 throw new IOException(exception.getMessage());
101 }
102 }
103
104 /***
105 * parses an experimentalFrame xml-file.
106 *
107 * @param input the url of the xmlfile
108 * @param context the root context for the experimentalFrame
109 * @return ExperimentalFrame the experimentalFrame
110 * @throws IOException whenever parsing fails
111 */
112 public static ExperimentalFrame parseExperimentalFrame(
113 final Context context, final URL input) throws IOException
114 {
115 if (input == null)
116 {
117 throw new IOException("experiment URL=null");
118 }
119 try
120 {
121 Calendar calendar = Calendar.getInstance();
122 calendar.setTimeInMillis(System.currentTimeMillis());
123 String name = DateFormat.getDateTimeInstance().format(
124 calendar.getTime());
125 Context root = context.createSubcontext(name);
126
127 Element rootElement = builder.build(input).getRootElement();
128 List<Experiment> experiments = new ArrayList<Experiment>();
129 @SuppressWarnings("unchecked")
130 List<Element> experimentElements = (List<Element>) rootElement
131 .getChildren("experiment");
132 int number = 0;
133
134 for (Element element : experimentElements)
135 {
136 Context experimentContext = root.createSubcontext("experiment["
137 + number + "]");
138 Experiment experiment = ExperimentParser.parseExperiment(
139 experimentContext, element, input);
140 experiment.setDescription("experiment " + number);
141 experiments.add(experiment);
142 number++;
143 }
144 ExperimentalFrame frame = new ExperimentalFrame(root, input);
145 frame.setExperiments(experiments);
146 return frame;
147 } catch (Exception exception)
148 {
149 Logger.warning(ExperimentParser.class, "parseExperimentalFrame",
150 exception);
151 return null;
152 }
153 }
154
155 /***
156 * parses an experiment xml-file.
157 *
158 * @param context the context for this experiment
159 * @param url the url of the experimentfile
160 * @param rootElement the element representing the experiment
161 * @return ExperimentalFrame the experiment
162 * @throws IOException whenever parsing fails
163 */
164 public static Experiment parseExperiment(final Context context,
165 final Element rootElement, final URL url) throws IOException
166 {
167 try
168 {
169 ClassLoader loader = ExperimentParser.resolveClassLoader(url);
170 Experiment experiment = new Experiment(context);
171 Element modelElement = rootElement.getChild("model");
172 if (modelElement.getChild("class-path") != null)
173 {
174 @SuppressWarnings("unchecked")
175 List<Element> jarFiles = (List<Element>) modelElement.getChild(
176 "class-path").getChildren("jar-file");
177 URL[] urls = new URL[jarFiles.size()];
178 int nr = 0;
179 for (Iterator<Element> i = jarFiles.iterator(); i.hasNext();)
180 {
181 Element child = i.next();
182 urls[nr] = URLResource.getResource(child.getValue());
183 nr++;
184 }
185 loader = new URLClassLoader(urls, loader);
186 }
187 Thread.currentThread().setContextClassLoader(loader);
188 Class modelClass = Class.forName(modelElement
189 .getChildText("model-class"), true, loader);
190
191 ModelInterface model = (ModelInterface) ClassUtil
192 .resolveConstructor(modelClass, null).newInstance();
193 experiment.setModel(model);
194
195 Treatment treatment = ExperimentParser.parseTreatment(rootElement
196 .getChild("treatment"), experiment, context);
197 experiment.setTreatment(treatment);
198
199 Class simulatorClass = Class.forName(rootElement
200 .getChildText("simulator-class"), true, loader);
201 SimulatorInterface simulator = (SimulatorInterface) ClassUtil
202 .resolveConstructor(simulatorClass, null).newInstance();
203 experiment.setSimulator(simulator);
204 return experiment;
205 } catch (Exception exception)
206 {
207 Logger
208 .warning(ExperimentParser.class, "parseExperiment",
209 exception);
210 throw new IOException(exception.getMessage());
211 }
212 }
213
214
215 /***
216 * parses an experiment xml-file.
217 *
218 * @param input the inputstream
219 * @return Classloader the classLoader
220 */
221 private static ClassLoader resolveClassLoader(final URL input)
222 {
223 ClassLoader loader = Thread.currentThread().getContextClassLoader();
224 if (input.getProtocol().equals("file"))
225 {
226 try
227 {
228 List<URL> urls = new ArrayList<URL>();
229 String path = input.getPath().substring(0,
230 input.getPath().lastIndexOf('/'))
231 + "/";
232
233 urls.add(new URL("file:" + path));
234
235 if (path.endsWith("src/"))
236 {
237 path = path.substring(0, path.length() - 4) + "bin/";
238 urls.add(new URL("file:" + path));
239 } else
240 {
241
242 path = path.substring(0, path.length()) + "bin/";
243 urls.add(new URL("file:" + path));
244 }
245 URL[] urlArray = urls.toArray(new URL[urls.size()]);
246 URLClassLoader urlClassLoader = new URLClassLoader(urlArray,
247 loader);
248 return urlClassLoader;
249 } catch (MalformedURLException exception)
250 {
251 return loader;
252 }
253 }
254 return loader;
255 }
256
257 /***
258 * parses the dateTime
259 *
260 * @param value the string value in the yyyy-mm-ddThh:mm:ss format
261 * @return long the amount of milliseconds since 1970.
262 */
263 private static long parseDateTime(final String value)
264 {
265 Calendar calendar = Calendar.getInstance();
266 String concatDate = value.split("T")[0];
267 String concatTime = value.split("T")[1];
268 String[] date = concatDate.split("-");
269 String[] time = concatTime.split(":");
270 calendar.set(new Integer(date[0]).intValue(), new Integer(date[1])
271 .intValue() - 1, new Integer(date[2]).intValue(), new Integer(
272 time[0]).intValue(), new Integer(time[1]).intValue(),
273 new Integer(time[2]).intValue());
274 return calendar.getTimeInMillis();
275 }
276
277 /***
278 * parses a period
279 *
280 * @param element the xml-element representing the period
281 * @param treatmentTimeUnit the timeUnit of the treatment
282 * @return double the value in units defined by the treatment
283 * @throws Exception whenever the period.
284 */
285 private static double parsePeriod(final Element element,
286 final TimeUnitInterface treatmentTimeUnit) throws Exception
287 {
288 TimeUnitInterface timeUnit = ExperimentParser.parseTimeUnit(element
289 .getAttribute("unit").getValue());
290 double value = -1;
291 if (element.getText().equals("INF"))
292 {
293 value = Double.MAX_VALUE;
294 } else
295 {
296 value = new Double(element.getText()).doubleValue();
297 }
298 if (value < 0)
299 {
300 throw new JDOMException("parsePeriod: value = " + value
301 + " <0. simulator cannot schedule in past");
302 }
303 return timeUnit.getValue() * value / treatmentTimeUnit.getValue();
304 }
305
306 /***
307 * parses a replication
308 *
309 * @param element the JDOM element
310 * @param parent the RunControl
311 * @param number the number
312 * @return the replication
313 * @throws Exception on failure
314 */
315 private static Replication parseReplication(final Element element,
316 final Treatment parent, final Context context) throws Exception
317 {
318 Replication replication = new Replication(context, parent);
319 if (element.getAttribute("description") != null)
320 {
321 replication.setDescription(element.getAttribute("description")
322 .getValue());
323 }
324 Map<String, StreamInterface> streams = new HashMap<String, StreamInterface>();
325 @SuppressWarnings("unchecked")
326 List<Element> streamElements = (List<Element>) element
327 .getChildren("stream");
328 for (Element streamElement : streamElements)
329 {
330 long seed = new Long(streamElement.getAttributeValue("seed"))
331 .longValue();
332 StreamInterface stream = new MersenneTwister(seed);
333 streams.put(streamElement.getAttributeValue("name"), stream);
334 }
335 replication.setStreams(streams);
336 return replication;
337 }
338
339 /***
340 * parses proprties to treatments
341 *
342 * @param element the element
343 * @return Properties
344 */
345 private static Properties parseProperties(final Element element)
346 {
347 Properties result = new Properties();
348 @SuppressWarnings("unchecked")
349 List<Element> children = (List<Element>) element
350 .getChildren("property");
351 for (Iterator<Element> i = children.iterator(); i.hasNext();)
352 {
353 Element child = i.next();
354 String key = child.getAttributeValue("key");
355 String value = child.getAttributeValue("value");
356 result.put(key, value);
357 }
358 return result;
359 }
360
361 /***
362 * parses a timeUnit
363 *
364 * @param name the name
365 * @return TimeUnitInterface result
366 * @throws Exception on failure
367 */
368 private static TimeUnitInterface parseTimeUnit(final String name)
369 throws Exception
370 {
371 if (name.equals("DAY"))
372 {
373 return TimeUnitInterface.DAY;
374 }
375 if (name.equals("HOUR"))
376 {
377 return TimeUnitInterface.HOUR;
378 }
379 if (name.equals("MILLISECOND"))
380 {
381 return TimeUnitInterface.MILLISECOND;
382 }
383 if (name.equals("MINUTE"))
384 {
385 return TimeUnitInterface.MINUTE;
386 }
387 if (name.equals("SECOND"))
388 {
389 return TimeUnitInterface.SECOND;
390 }
391 if (name.equals("WEEK"))
392 {
393 return TimeUnitInterface.WEEK;
394 }
395 if (name.equals("UNIT"))
396 {
397 return TimeUnitInterface.UNIT;
398 }
399 throw new Exception("parseTimeUnit.. unknown argument: " + name);
400 }
401
402 /***
403 * parses the treatment
404 *
405 * @param element the xml-element
406 * @param parent parent
407 * @param number the number
408 * @return Treatment
409 * @throws Exception on failure
410 */
411 private static Treatment parseTreatment(final Element element,
412 final Experiment parent, final Context context) throws Exception
413 {
414 Treatment treatment = new Treatment(parent);
415 treatment.setTimeUnit(ExperimentParser.parseTimeUnit(element
416 .getChildText("timeUnit")));
417 if (element.getChild("startTime") != null)
418 {
419 treatment.setStartTime(ExperimentParser.parseDateTime(element
420 .getChildText("startTime")));
421 }
422 if (element.getChild("warmupPeriod") != null)
423 {
424 treatment.setWarmupPeriod(ExperimentParser.parsePeriod(element
425 .getChild("warmupPeriod"), treatment.getTimeUnit()));
426 }
427 if (element.getChild("runLength") != null)
428 {
429 treatment.setRunLength(ExperimentParser.parsePeriod(element
430 .getChild("runLength"), treatment.getTimeUnit()));
431 }
432 @SuppressWarnings("unchecked")
433 List<Element> replicationElements = (List<Element>) element
434 .getChildren("replication");
435 List<Replication> replicationArray = new ArrayList<Replication>();
436 int number = 1;
437 for (Iterator<Element> i = replicationElements.iterator(); i.hasNext();)
438 {
439 replicationArray.add(ExperimentParser.parseReplication(i.next(),
440 treatment, context));
441 number++;
442 }
443 treatment.setReplications(replicationArray);
444 if (element.getChild("properties") != null)
445 {
446 treatment.setProperties(ExperimentParser.parseProperties(element
447 .getChild("properties")));
448 }
449 return treatment;
450 }
451 }