1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml.io;
18
19 import java.io.StringWriter;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Properties;
24 import java.util.Set;
25
26 import javax.xml.transform.OutputKeys;
27 import javax.xml.transform.Result;
28 import javax.xml.transform.Source;
29 import javax.xml.transform.Transformer;
30 import javax.xml.transform.TransformerConfigurationException;
31 import javax.xml.transform.TransformerException;
32 import javax.xml.transform.TransformerFactory;
33 import javax.xml.transform.TransformerFactoryConfigurationError;
34 import javax.xml.transform.dom.DOMSource;
35 import javax.xml.transform.stream.StreamResult;
36
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.commons.scxml.SCXMLHelper;
39 import org.apache.commons.scxml.model.Action;
40 import org.apache.commons.scxml.model.Assign;
41 import org.apache.commons.scxml.model.Cancel;
42 import org.apache.commons.scxml.model.Data;
43 import org.apache.commons.scxml.model.Datamodel;
44 import org.apache.commons.scxml.model.Else;
45 import org.apache.commons.scxml.model.ElseIf;
46 import org.apache.commons.scxml.model.Exit;
47 import org.apache.commons.scxml.model.ExternalContent;
48 import org.apache.commons.scxml.model.Finalize;
49 import org.apache.commons.scxml.model.History;
50 import org.apache.commons.scxml.model.If;
51 import org.apache.commons.scxml.model.Initial;
52 import org.apache.commons.scxml.model.Invoke;
53 import org.apache.commons.scxml.model.Log;
54 import org.apache.commons.scxml.model.NamespacePrefixesHolder;
55 import org.apache.commons.scxml.model.OnEntry;
56 import org.apache.commons.scxml.model.OnExit;
57 import org.apache.commons.scxml.model.Parallel;
58 import org.apache.commons.scxml.model.Param;
59 import org.apache.commons.scxml.model.SCXML;
60 import org.apache.commons.scxml.model.Send;
61 import org.apache.commons.scxml.model.State;
62 import org.apache.commons.scxml.model.Transition;
63 import org.apache.commons.scxml.model.TransitionTarget;
64 import org.apache.commons.scxml.model.Var;
65 import org.w3c.dom.Node;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public class SCXMLSerializer {
86
87
88 private static final String INDENT = " ";
89
90 private static final Transformer XFORMER = getTransformer();
91
92 private static final String NAMESPACE_SCXML =
93 "http://www.w3.org/2005/07/scxml";
94
95 private static final String NAMESPACE_COMMONS_SCXML =
96 "http://commons.apache.org/scxml";
97
98
99
100
101
102
103
104
105 public static String serialize(final SCXML scxml) {
106 StringBuffer b =
107 new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n").
108 append("<scxml xmlns=\"").append(NAMESPACE_SCXML).
109 append("\"").append(serializeNamespaceDeclarations(scxml)).
110 append(" version=\"").append(scxml.getVersion()).
111 append("\" initial=\"").append(scxml.getInitial()).
112 append("\">\n");
113 if (XFORMER == null) {
114 org.apache.commons.logging.Log log = LogFactory.
115 getLog(SCXMLSerializer.class);
116 log.warn("SCXMLSerializer: DOM serialization pertinent to"
117 + " the document will be skipped since a suitable"
118 + " JAXP Transformer could not be instantiated.");
119 }
120 b.append(INDENT).append("<!-- http://commons.apache.org/scxml -->\n");
121 Datamodel dm = scxml.getDatamodel();
122 if (dm != null) {
123 serializeDatamodel(b, dm, INDENT);
124 }
125 Map c = scxml.getChildren();
126 Iterator i = c.keySet().iterator();
127 while (i.hasNext()) {
128 TransitionTarget tt = (TransitionTarget) c.get(i.next());
129 if (tt instanceof State) {
130 serializeState(b, (State) tt, INDENT);
131 } else {
132 serializeParallel(b, (Parallel) tt, INDENT);
133 }
134 }
135 b.append("</scxml>\n");
136 return b.toString();
137 }
138
139
140
141
142
143
144
145
146 public static void serializeState(final StringBuffer b,
147 final State s, final String indent) {
148 b.append(indent).append("<state");
149 serializeTransitionTargetAttributes(b, s);
150 boolean f = s.isFinal();
151 if (f) {
152 b.append(" final=\"true\"");
153 }
154 b.append(">\n");
155 Initial ini = s.getInitial();
156 if (ini != null) {
157 serializeInitial(b, ini, indent + INDENT);
158 }
159 List h = s.getHistory();
160 if (h != null) {
161 serializeHistory(b, h, indent + INDENT);
162 }
163 Datamodel dm = s.getDatamodel();
164 if (dm != null) {
165 serializeDatamodel(b, dm, indent + INDENT);
166 }
167 serializeOnEntry(b, s, indent + INDENT);
168 List t = s.getTransitionsList();
169 for (int i = 0; i < t.size(); i++) {
170 serializeTransition(b, (Transition) t.get(i), indent + INDENT);
171 }
172 Parallel p = s.getParallel();
173 Invoke inv = s.getInvoke();
174 if (p != null) {
175 serializeParallel(b, p, indent + INDENT);
176 } else if (inv != null) {
177 serializeInvoke(b , inv, indent + INDENT);
178 } else {
179 Map c = s.getChildren();
180 Iterator j = c.keySet().iterator();
181 while (j.hasNext()) {
182 TransitionTarget tt = (TransitionTarget) c.get(j.next());
183 if (tt instanceof State) {
184 serializeState(b, (State) tt, indent + INDENT);
185 } else if (tt instanceof Parallel) {
186 serializeParallel(b, (Parallel) tt, indent + INDENT);
187 }
188 }
189 }
190 serializeOnExit(b, s, indent + INDENT);
191 b.append(indent).append("</state>\n");
192 }
193
194
195
196
197
198
199
200
201 public static void serializeParallel(final StringBuffer b,
202 final Parallel p, final String indent) {
203 b.append(indent).append("<parallel");
204 serializeTransitionTargetAttributes(b, p);
205 b.append(">\n");
206 serializeOnEntry(b, p, indent + INDENT);
207 Set s = p.getChildren();
208 Iterator i = s.iterator();
209 while (i.hasNext()) {
210 serializeState(b, (State) i.next(), indent + INDENT);
211 }
212 serializeOnExit(b, p, indent + INDENT);
213 b.append(indent).append("</parallel>\n");
214 }
215
216
217
218
219
220
221
222
223 public static void serializeInvoke(final StringBuffer b,
224 final Invoke i, final String indent) {
225 b.append(indent).append("<invoke");
226 String type = i.getType();
227 String src = i.getSrc();
228 String srcexpr = i.getSrcexpr();
229 if (type != null) {
230 b.append(" type=\"").append(type).append("\"");
231 }
232
233 if (src != null) {
234 b.append(" src=\"").append(src).append("\"");
235 } else if (srcexpr != null) {
236 b.append(" srcexpr=\"").append(srcexpr).append("\"");
237 }
238 b.append(">\n");
239 List params = i.params();
240 for (Iterator iter = params.iterator(); iter.hasNext();) {
241 Param p = (Param) iter.next();
242 b.append(indent).append(INDENT).append("<param name=\"").
243 append(p.getName()).append("\" expr=\"").
244 append(SCXMLHelper.escapeXML(p.getExpr())).append("\"/>\n");
245 }
246 Finalize f = i.getFinalize();
247 if (f != null) {
248 b.append(indent).append(INDENT).append("<finalize>\n");
249 serializeActions(b, f.getActions(), indent + INDENT + INDENT);
250 b.append(indent).append(INDENT).append("</finalize>\n");
251 }
252 b.append(indent).append("</invoke>\n");
253 }
254
255
256
257
258
259
260
261
262 public static void serializeInitial(final StringBuffer b, final Initial i,
263 final String indent) {
264 b.append(indent).append("<initial");
265 serializeTransitionTargetAttributes(b, i);
266 b.append(">\n");
267 serializeTransition(b, i.getTransition(), indent + INDENT);
268 b.append(indent).append("</initial>\n");
269 }
270
271
272
273
274
275
276
277
278 public static void serializeHistory(final StringBuffer b, final List l,
279 final String indent) {
280 if (l.size() > 0) {
281 for (int i = 0; i < l.size(); i++) {
282 History h = (History) l.get(i);
283 b.append(indent).append("<history");
284 serializeTransitionTargetAttributes(b, h);
285 if (h.isDeep()) {
286 b.append(" type=\"deep\"");
287 } else {
288 b.append(" type=\"shallow\"");
289 }
290 b.append(">\n");
291 serializeTransition(b, h.getTransition(), indent + INDENT);
292 b.append(indent).append("</history>\n");
293 }
294 }
295 }
296
297
298
299
300
301
302
303
304 public static void serializeTransition(final StringBuffer b,
305 final Transition t, final String indent) {
306 b.append(indent).append("<transition");
307 if (!SCXMLHelper.isStringEmpty(t.getEvent())) {
308 b.append(" event=\"").append(t.getEvent()).append("\"");
309 }
310 if (!SCXMLHelper.isStringEmpty(t.getCond())) {
311 b.append(" cond=\"").append(SCXMLHelper.escapeXML(t.getCond())).
312 append("\"");
313 }
314 boolean next = !SCXMLHelper.isStringEmpty(t.getNext());
315 if (next) {
316 b.append(" target=\"" + t.getNext() + "\"");
317 }
318 b.append(">\n");
319 boolean exit = serializeActions(b, t.getActions(), indent + INDENT);
320 if (!next && !exit) {
321 serializeTarget(b, t, indent + INDENT);
322 }
323 b.append(indent).append("</transition>\n");
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337 public static void serializeTarget(final StringBuffer b,
338 final Transition t, final String indent) {
339 if (t.getTarget() != null) {
340 b.append(indent).append("<target>");
341
342 serializeState(b, (State) t.getTarget(), indent + INDENT);
343 b.append(indent).append("</target>");
344 }
345 }
346
347
348
349
350
351
352
353
354 public static void serializeDatamodel(final StringBuffer b,
355 final Datamodel dm, final String indent) {
356 List data = dm.getData();
357 if (data != null && data.size() > 0) {
358 b.append(indent).append("<datamodel>\n");
359 if (XFORMER == null) {
360 b.append(indent).append(INDENT).
361 append("<!-- Body content was not serialized -->\n");
362 b.append(indent).append("</datamodel>\n");
363 return;
364 }
365 for (Iterator iter = data.iterator(); iter.hasNext();) {
366 Data datum = (Data) iter.next();
367 Node dataNode = datum.getNode();
368 if (dataNode != null) {
369 StringWriter out = new StringWriter();
370 try {
371 Source input = new DOMSource(dataNode);
372 Result output = new StreamResult(out);
373 XFORMER.transform(input, output);
374 } catch (TransformerException te) {
375 org.apache.commons.logging.Log log = LogFactory.
376 getLog(SCXMLSerializer.class);
377 log.error(te.getMessage(), te);
378 b.append(indent).append(INDENT).
379 append("<!-- Data content not serialized -->\n");
380 }
381 b.append(indent).append(INDENT).append(out.toString());
382 } else {
383 b.append(indent).append(INDENT).append("<data id=\"").
384 append(datum.getId()).append("\" expr=\"").
385 append(SCXMLHelper.escapeXML(datum.getExpr())).
386 append("\" />\n");
387 }
388 }
389 b.append(indent).append("</datamodel>\n");
390 }
391 }
392
393
394
395
396
397
398
399
400 public static void serializeOnEntry(final StringBuffer b,
401 final TransitionTarget t, final String indent) {
402 OnEntry e = t.getOnEntry();
403 if (e != null && e.getActions().size() > 0) {
404 b.append(indent).append("<onentry>\n");
405 serializeActions(b, e.getActions(), indent + INDENT);
406 b.append(indent).append("</onentry>\n");
407 }
408 }
409
410
411
412
413
414
415
416
417 public static void serializeOnExit(final StringBuffer b,
418 final TransitionTarget t, final String indent) {
419 OnExit x = t.getOnExit();
420 if (x != null && x.getActions().size() > 0) {
421 b.append(indent).append("<onexit>\n");
422 serializeActions(b, x.getActions(), indent + INDENT);
423 b.append(indent).append("</onexit>\n");
424 }
425 }
426
427
428
429
430
431
432
433
434
435 public static boolean serializeActions(final StringBuffer b, final List l,
436 final String indent) {
437 if (l == null) {
438 return false;
439 }
440 boolean exit = false;
441 Iterator i = l.iterator();
442 while (i.hasNext()) {
443 Action a = (Action) i.next();
444 if (a instanceof Var) {
445 Var v = (Var) a;
446 b.append(indent).append("<cs:var name=\"").append(v.getName())
447 .append("\" expr=\"")
448 .append(SCXMLHelper.escapeXML(v.getExpr()))
449 .append("\"/>\n");
450 } else if (a instanceof Assign) {
451 Assign asn = (Assign) a;
452 b.append(indent).append("<assign");
453 if (!SCXMLHelper.isStringEmpty(asn.getLocation())) {
454 b.append(" location=\"").append(asn.getLocation());
455 if (!SCXMLHelper.isStringEmpty(asn.getSrc())) {
456 b.append("\" src=\"").append(asn.getSrc());
457 } else {
458 b.append("\" expr=\"").
459 append(SCXMLHelper.escapeXML(asn.getExpr()));
460 }
461 } else {
462 b.append(" name=\"").append(asn.getName()).
463 append("\" expr=\"").
464 append(SCXMLHelper.escapeXML(asn.getExpr()));
465 }
466 b.append("\"/>\n");
467 } else if (a instanceof Send) {
468 serializeSend(b, (Send) a, indent);
469 } else if (a instanceof Cancel) {
470 Cancel c = (Cancel) a;
471 b.append(indent).append("<cancel sendid=\"")
472 .append(c.getSendid()).append("\"/>\n");
473 } else if (a instanceof Log) {
474 Log lg = (Log) a;
475 b.append(indent).append("<log expr=\"").
476 append(SCXMLHelper.escapeXML(lg.getExpr())).
477 append("\"/>\n");
478 } else if (a instanceof Exit) {
479 Exit e = (Exit) a;
480 b.append(indent).append("<cs:exit");
481 String expr = SCXMLHelper.escapeXML(e.getExpr());
482 String nl = e.getNamelist();
483 if (expr != null) {
484 b.append(" expr=\"" + expr + "\"");
485 }
486 if (nl != null) {
487 b.append(" namelist=\"" + nl + "\"");
488 }
489 b.append("/>\n");
490 exit = true;
491 } else if (a instanceof If) {
492 If iff = (If) a;
493 serializeIf(b, iff, indent);
494 } else if (a instanceof Else) {
495 b.append(indent).append("<else/>\n");
496 } else if (a instanceof ElseIf) {
497 ElseIf eif = (ElseIf) a;
498 b.append(indent).append("<elseif cond=\"")
499 .append(SCXMLHelper.escapeXML(eif.getCond()))
500 .append("\" />\n");
501 }
502 }
503 return exit;
504 }
505
506
507
508
509
510
511
512
513 public static void serializeSend(final StringBuffer b,
514 final Send send, final String indent) {
515 b.append(indent).append("<send");
516 if (send.getSendid() != null) {
517 b.append(" sendid=\"").append(send.getSendid()).append("\"");
518 }
519 if (send.getTarget() != null) {
520 b.append(" target=\"").append(send.getTarget()).append("\"");
521 }
522 if (send.getType() != null) {
523 b.append(" type=\"").append(send.getType()).append("\"");
524 }
525 if (send.getNamelist() != null) {
526 b.append(" namelist=\"").append(send.getNamelist()).append("\"");
527 }
528 if (send.getDelay() != null) {
529 b.append(" delay=\"").append(send.getDelay()).append("\"");
530 }
531 if (send.getEvent() != null) {
532 b.append(" event=\"").append(send.getEvent()).append("\"");
533 }
534 if (send.getHints() != null) {
535 b.append(" hints=\"").append(send.getHints()).append("\"");
536 }
537 b.append(">\n");
538 b.append(getBodyContent(send));
539 b.append(indent).append("</send>\n");
540 }
541
542
543
544
545
546
547
548 public static final String getBodyContent(
549 final ExternalContent externalContent) {
550 StringBuffer buf = new StringBuffer();
551 List externalNodes = externalContent.getExternalNodes();
552 if (externalNodes.size() > 0 && XFORMER == null) {
553 buf.append("<!-- Body content was not serialized -->\n");
554 return buf.toString();
555 }
556 for (int i = 0; i < externalNodes.size(); i++) {
557 Source input = new DOMSource((Node) externalNodes.get(i));
558 StringWriter out = new StringWriter();
559 Result output = new StreamResult(out);
560 try {
561 XFORMER.transform(input, output);
562 } catch (TransformerException te) {
563 org.apache.commons.logging.Log log = LogFactory.
564 getLog(SCXMLSerializer.class);
565 log.error(te.getMessage(), te);
566 buf.append("<!-- Not all body content was serialized -->");
567 }
568 buf.append(out.toString()).append("\n");
569 }
570 return buf.toString();
571 }
572
573
574
575
576
577
578
579
580 public static void serializeIf(final StringBuffer b,
581 final If iff, final String indent) {
582 b.append(indent).append("<if cond=\"").append(SCXMLHelper.
583 escapeXML(iff.getCond())).append("\">\n");
584 serializeActions(b, iff.getActions(), indent + INDENT);
585 b.append(indent).append("</if>\n");
586 }
587
588
589
590
591
592
593
594 private static void serializeTransitionTargetAttributes(
595 final StringBuffer b, final TransitionTarget t) {
596 String id = t.getId();
597 if (id != null) {
598 b.append(" id=\"").append(id).append("\"");
599 }
600 }
601
602
603
604
605
606
607
608 private static String serializeNamespaceDeclarations(
609 final NamespacePrefixesHolder holder) {
610 Map ns = holder.getNamespaces();
611 StringBuffer b = new StringBuffer();
612 if (ns != null) {
613 Iterator iter = ns.entrySet().iterator();
614 while (iter.hasNext()) {
615 Map.Entry entry = (Map.Entry) iter.next();
616 String prefix = (String) entry.getKey();
617 String nsURI = (String) entry.getValue();
618 if (prefix.length() == 0 && !nsURI.equals(NAMESPACE_SCXML)) {
619 org.apache.commons.logging.Log log = LogFactory.
620 getLog(SCXMLSerializer.class);
621 log.warn("When using the SCXMLSerializer, the default "
622 + "namespace must be the SCXML namespace:"
623 + NAMESPACE_SCXML);
624 } if (prefix.equals("cs")
625 && !nsURI.equals(NAMESPACE_COMMONS_SCXML)) {
626 org.apache.commons.logging.Log log = LogFactory.
627 getLog(SCXMLSerializer.class);
628 log.warn("When using the SCXMLSerializer, the namespace"
629 + "prefix \"cs\" must bind to the Commons SCXML "
630 + "namespace:" + NAMESPACE_COMMONS_SCXML);
631 } else if (prefix.length() > 0) {
632 b.append(" xmlns:").append(prefix).append("=\"").
633 append(nsURI).append("\"");
634 }
635 }
636 }
637 return b.toString();
638 }
639
640
641
642
643
644
645 private static Transformer getTransformer() {
646 Transformer transformer = null;
647 Properties outputProps = new Properties();
648 outputProps.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
649 outputProps.put(OutputKeys.STANDALONE, "no");
650 outputProps.put(OutputKeys.INDENT, "yes");
651 try {
652 TransformerFactory tfFactory = TransformerFactory.newInstance();
653 transformer = tfFactory.newTransformer();
654 transformer.setOutputProperties(outputProps);
655 } catch (TransformerFactoryConfigurationError t) {
656 return null;
657 } catch (TransformerConfigurationException e) {
658 return null;
659 }
660 return transformer;
661 }
662
663
664
665
666
667
668
669 private SCXMLSerializer() {
670 super();
671 }
672
673 }