1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml2.io;
18
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.io.StringReader;
22 import java.io.StringWriter;
23 import java.io.Writer;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Properties;
28
29 import javax.xml.stream.XMLOutputFactory;
30 import javax.xml.stream.XMLStreamException;
31 import javax.xml.stream.XMLStreamWriter;
32 import javax.xml.transform.OutputKeys;
33 import javax.xml.transform.Result;
34 import javax.xml.transform.Source;
35 import javax.xml.transform.Transformer;
36 import javax.xml.transform.TransformerConfigurationException;
37 import javax.xml.transform.TransformerException;
38 import javax.xml.transform.TransformerFactory;
39 import javax.xml.transform.TransformerFactoryConfigurationError;
40 import javax.xml.transform.dom.DOMSource;
41 import javax.xml.transform.stream.StreamResult;
42 import javax.xml.transform.stream.StreamSource;
43
44 import org.apache.commons.logging.LogFactory;
45 import org.apache.commons.scxml2.model.Action;
46 import org.apache.commons.scxml2.model.Assign;
47 import org.apache.commons.scxml2.model.Cancel;
48 import org.apache.commons.scxml2.model.Content;
49 import org.apache.commons.scxml2.model.Data;
50 import org.apache.commons.scxml2.model.Datamodel;
51 import org.apache.commons.scxml2.model.Else;
52 import org.apache.commons.scxml2.model.ElseIf;
53 import org.apache.commons.scxml2.model.EnterableState;
54 import org.apache.commons.scxml2.model.Raise;
55 import org.apache.commons.scxml2.model.ExternalContent;
56 import org.apache.commons.scxml2.model.Final;
57 import org.apache.commons.scxml2.model.Finalize;
58 import org.apache.commons.scxml2.model.Foreach;
59 import org.apache.commons.scxml2.model.History;
60 import org.apache.commons.scxml2.model.If;
61 import org.apache.commons.scxml2.model.Initial;
62 import org.apache.commons.scxml2.model.Invoke;
63 import org.apache.commons.scxml2.model.Log;
64 import org.apache.commons.scxml2.model.OnEntry;
65 import org.apache.commons.scxml2.model.OnExit;
66 import org.apache.commons.scxml2.model.Parallel;
67 import org.apache.commons.scxml2.model.Param;
68 import org.apache.commons.scxml2.model.SCXML;
69 import org.apache.commons.scxml2.model.Script;
70 import org.apache.commons.scxml2.model.Send;
71 import org.apache.commons.scxml2.model.SimpleTransition;
72 import org.apache.commons.scxml2.model.State;
73 import org.apache.commons.scxml2.model.Transition;
74 import org.apache.commons.scxml2.model.TransitionTarget;
75 import org.apache.commons.scxml2.model.Var;
76 import org.w3c.dom.Node;
77 import org.w3c.dom.NodeList;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 public class SCXMLWriter {
100
101
102
103
104
105
106 private static final String XMLNS_SCXML = "http://www.w3.org/2005/07/scxml";
107
108
109
110
111 private static final String XMLNS_COMMONS_SCXML = "http://commons.apache.org/scxml";
112
113
114
115
116
117 private static final String ERR_NULL_OSTR = "Cannot write to null OutputStream";
118
119
120
121
122 private static final String ERR_NULL_WRIT = "Cannot write to null Writer";
123
124
125
126
127 private static final String ERR_NULL_RES = "Cannot parse null Result";
128
129
130
131 private static final String ELEM_ASSIGN = "assign";
132 private static final String ELEM_CANCEL = "cancel";
133 private static final String ELEM_CONTENT = "content";
134 private static final String ELEM_DATA = "data";
135 private static final String ELEM_DATAMODEL = "datamodel";
136 private static final String ELEM_ELSE = "else";
137 private static final String ELEM_ELSEIF = "elseif";
138 private static final String ELEM_RAISE = "raise";
139 private static final String ELEM_FINAL = "final";
140 private static final String ELEM_FINALIZE = "finalize";
141 private static final String ELEM_HISTORY = "history";
142 private static final String ELEM_IF = "if";
143 private static final String ELEM_INITIAL = "initial";
144 private static final String ELEM_INVOKE = "invoke";
145 private static final String ELEM_FOREACH = "foreach";
146 private static final String ELEM_LOG = "log";
147 private static final String ELEM_ONENTRY = "onentry";
148 private static final String ELEM_ONEXIT = "onexit";
149 private static final String ELEM_PARALLEL = "parallel";
150 private static final String ELEM_PARAM = "param";
151 private static final String ELEM_SCRIPT = "script";
152 private static final String ELEM_SCXML = "scxml";
153 private static final String ELEM_SEND = "send";
154 private static final String ELEM_STATE = "state";
155 private static final String ELEM_TRANSITION = "transition";
156 private static final String ELEM_VAR = "var";
157
158
159 private static final String ATTR_ARRAY = "array";
160 private static final String ATTR_ATTR = "attr";
161 private static final String ATTR_AUTOFORWARD = "autoforward";
162 private static final String ATTR_COND = "cond";
163 private static final String ATTR_DATAMODEL = "datamodel";
164 private static final String ATTR_DELAY = "delay";
165 private static final String ATTR_DELAYEXPR = "delayexpr";
166 private static final String ATTR_EVENT = "event";
167 private static final String ATTR_EVENTEXPR = "eventexpr";
168 private static final String ATTR_EXMODE = "exmode";
169 private static final String ATTR_EXPR = "expr";
170 private static final String ATTR_HINTS = "hints";
171 private static final String ATTR_ID = "id";
172 private static final String ATTR_IDLOCATION = "idlocation";
173 private static final String ATTR_INDEX = "index";
174 private static final String ATTR_INITIAL = "initial";
175 private static final String ATTR_ITEM = "item";
176 private static final String ATTR_LABEL = "label";
177 private static final String ATTR_LOCATION = "location";
178 private static final String ATTR_NAME = "name";
179 private static final String ATTR_NAMELIST = "namelist";
180 private static final String ATTR_PROFILE = "profile";
181 private static final String ATTR_SENDID = "sendid";
182 private static final String ATTR_SRC = "src";
183 private static final String ATTR_SRCEXPR = "srcexpr";
184 private static final String ATTR_TARGET = "target";
185 private static final String ATTR_TARGETEXPR = "targetexpr";
186 private static final String ATTR_TYPE = "type";
187 private static final String ATTR_TYPEEXPR = "typeexpr";
188 private static final String ATTR_VERSION = "version";
189
190
191
192
193
194 private static final Transformer XFORMER = getTransformer();
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209 public static String write(final SCXML scxml)
210 throws IOException, XMLStreamException {
211
212 return write(scxml, new Configuration(true, true));
213 }
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228 public static String write(final SCXML scxml, final Configuration configuration)
229 throws IOException, XMLStreamException {
230
231
232 configuration.writeToString = true;
233 writeInternal(scxml, configuration, null, null, null);
234 if (configuration.usePrettyPrint) {
235 return configuration.prettyPrintOutput;
236 } else {
237 configuration.internalWriter.flush();
238 return configuration.internalWriter.toString();
239 }
240 }
241
242
243
244
245
246
247
248
249
250
251
252 public static void write(final SCXML scxml, final OutputStream scxmlStream)
253 throws IOException, XMLStreamException {
254
255 write(scxml, scxmlStream, new Configuration());
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269 public static void write(final SCXML scxml, final OutputStream scxmlStream, final Configuration configuration)
270 throws IOException, XMLStreamException {
271
272 if (scxmlStream == null) {
273 throw new IllegalArgumentException(ERR_NULL_OSTR);
274 }
275 writeInternal(scxml, configuration, scxmlStream, null, null);
276 if (configuration.closeUnderlyingWhenDone) {
277 scxmlStream.flush();
278 scxmlStream.close();
279 }
280 }
281
282
283
284
285
286
287
288
289
290
291
292 public static void write(final SCXML scxml, final Writer scxmlWriter)
293 throws IOException, XMLStreamException {
294
295 write(scxml, scxmlWriter, new Configuration());
296 }
297
298
299
300
301
302
303
304
305
306
307
308
309 public static void write(final SCXML scxml, final Writer scxmlWriter, final Configuration configuration)
310 throws IOException, XMLStreamException {
311
312 if (scxmlWriter == null) {
313 throw new IllegalArgumentException(ERR_NULL_WRIT);
314 }
315 writeInternal(scxml, configuration, null, scxmlWriter, null);
316 if (configuration.closeUnderlyingWhenDone) {
317 scxmlWriter.flush();
318 scxmlWriter.close();
319 }
320 }
321
322
323
324
325
326
327
328
329
330
331
332 public static void write(final SCXML scxml, final Result scxmlResult)
333 throws IOException, XMLStreamException {
334
335 write(scxml, scxmlResult, new Configuration());
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349 public static void write(final SCXML scxml, final Result scxmlResult, final Configuration configuration)
350 throws IOException, XMLStreamException {
351
352 if (scxmlResult == null) {
353 throw new IllegalArgumentException(ERR_NULL_RES);
354 }
355 writeInternal(scxml, configuration, null, null, scxmlResult);
356 }
357
358
359
360
361
362
363
364
365
366
367 private static String escapeXML(final String str) {
368 if (str == null) {
369 return null;
370 }
371
372
373 int len = str.length();
374 StringWriter stringWriter = new StringWriter(len + 8);
375
376 for (int i = 0; i < len; i++) {
377 char c = str.charAt(i);
378 String entityName = null;
379 switch (c) {
380 case '"':
381 entityName = "quot";
382 break;
383 case '&':
384 entityName = "amp";
385 break;
386 case '<':
387 entityName = "lt";
388 break;
389 case '>':
390 entityName = "gt";
391 break;
392 default:
393 }
394 if (entityName == null) {
395 if (c > 0x7F) {
396 stringWriter.write("&#");
397 stringWriter.write(Integer.toString(c));
398 stringWriter.write(';');
399 } else {
400 stringWriter.write(c);
401 }
402 } else {
403 stringWriter.write('&');
404 stringWriter.write(entityName);
405 stringWriter.write(';');
406 }
407 }
408
409 return stringWriter.toString();
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425 private static void writeInternal(final SCXML scxml, final Configuration configuration,
426 final OutputStream scxmlStream, final Writer scxmlWriter, final Result scxmlResult)
427 throws IOException, XMLStreamException {
428
429 XMLStreamWriter writer = getWriter(configuration, scxmlStream, scxmlWriter, scxmlResult);
430 writeDocument(writer, configuration, scxml);
431 writer.flush();
432 writer.close();
433 if (configuration.internalWriter != null) {
434 configuration.internalWriter.flush();
435 }
436 if (configuration.usePrettyPrint) {
437 Writer prettyPrintWriter = (scxmlWriter != null ? scxmlWriter : new StringWriter());
438 writePretty(configuration, scxmlStream, prettyPrintWriter, scxmlResult);
439 if (configuration.writeToString) {
440 prettyPrintWriter.flush();
441 configuration.prettyPrintOutput = prettyPrintWriter.toString();
442 }
443 }
444 }
445
446
447
448
449
450
451
452
453
454
455
456 private static void writeDocument(final XMLStreamWriter writer, final Configuration configuration,
457 final SCXML scxml)
458 throws XMLStreamException {
459
460 String encoding = "UTF-8";
461 if (configuration.encoding != null) {
462 encoding = configuration.encoding;
463 }
464 writer.writeStartDocument(encoding, "1.0");
465 writeSCXML(writer, scxml);
466 writer.writeEndDocument();
467 }
468
469
470
471
472
473
474
475
476
477 private static void writeSCXML(final XMLStreamWriter writer, final SCXML scxml)
478 throws XMLStreamException {
479
480
481 writer.writeStartElement(ELEM_SCXML);
482
483
484 writer.writeNamespace(null, XMLNS_SCXML);
485 writer.writeNamespace("cs", XMLNS_COMMONS_SCXML);
486 for (Map.Entry<String, String> entry : scxml.getNamespaces().entrySet()) {
487 String key = entry.getKey();
488 if (key != null && key.trim().length() > 0 && !key.equals("cs")) {
489 writer.writeNamespace(key, entry.getValue());
490 }
491 }
492
493
494 writeAV(writer, ATTR_VERSION, scxml.getVersion());
495 writeAV(writer, ATTR_INITIAL, scxml.getInitial());
496 writeAV(writer, ATTR_DATAMODEL, scxml.getDatamodelName());
497 writeAV(writer, ATTR_NAME, scxml.getName());
498 writeAV(writer, ATTR_PROFILE, scxml.getProfile());
499 writeAV(writer, ATTR_EXMODE, scxml.getExmode());
500
501
502 writer.writeComment(XMLNS_COMMONS_SCXML);
503
504
505 if (scxml.getGlobalScript() != null) {
506 Script s = scxml.getGlobalScript();
507 writer.writeStartElement(XMLNS_SCXML, ELEM_SCRIPT);
508 writer.writeCData(s.getScript());
509 writer.writeEndElement();
510 }
511
512
513 writeDatamodel(writer, scxml.getDatamodel());
514 for (EnterableState es : scxml.getChildren()) {
515 if (es instanceof Final) {
516 writeFinal(writer, (Final) es);
517 } else if (es instanceof State) {
518 writeState(writer, (State) es);
519 } else if (es instanceof Parallel) {
520 writeParallel(writer, (Parallel) es);
521 }
522 }
523
524
525 writer.writeEndElement();
526 }
527
528
529
530
531
532
533
534
535
536 private static void writeDatamodel(final XMLStreamWriter writer, final Datamodel datamodel)
537 throws XMLStreamException {
538
539 if (datamodel == null) {
540 return;
541 }
542
543 writer.writeStartElement(ELEM_DATAMODEL);
544 if (datamodel.getData().size() > 0 && XFORMER == null) {
545 writer.writeComment("Datamodel was not serialized");
546 } else {
547 for (Data d : datamodel.getData()) {
548 Node n = d.getNode();
549 if (n != null) {
550 writeNode(writer, n);
551 } else {
552 writer.writeStartElement(ELEM_DATA);
553 writeAV(writer, ATTR_ID, d.getId());
554 writeAV(writer, ATTR_SRC, escapeXML(d.getSrc()));
555 writeAV(writer, ATTR_EXPR, escapeXML(d.getExpr()));
556 writer.writeEndElement();
557 }
558 }
559 }
560 writer.writeEndElement();
561 }
562
563
564
565
566
567
568
569 private static void writeTransitionTargetId(final XMLStreamWriter writer, final TransitionTarget tt)
570 throws XMLStreamException {
571 if (!tt.getId().startsWith(SCXML.GENERATED_TT_ID_PREFIX)) {
572 writeAV(writer, ATTR_ID, tt.getId());
573 }
574 }
575
576
577
578
579
580
581
582
583
584 private static void writeState(final XMLStreamWriter writer, final State state)
585 throws XMLStreamException {
586
587 writer.writeStartElement(ELEM_STATE);
588 writeTransitionTargetId(writer, state);
589 writeAV(writer, ATTR_INITIAL, state.getFirst());
590 writeInitial(writer, state.getInitial());
591 writeDatamodel(writer, state.getDatamodel());
592 writeHistory(writer, state.getHistory());
593 for (OnEntry onentry : state.getOnEntries()) {
594 writeOnEntry(writer, onentry);
595 }
596
597 for (Transition t : state.getTransitionsList()) {
598 writeTransition(writer, t);
599 }
600
601 for (Invoke inv : state.getInvokes()) {
602 writeInvoke(writer, inv);
603 }
604
605 for (EnterableState es : state.getChildren()) {
606 if (es instanceof Final) {
607 writeFinal(writer, (Final) es);
608 } else if (es instanceof State) {
609 writeState(writer, (State) es);
610 } else if (es instanceof Parallel) {
611 writeParallel(writer, (Parallel) es);
612 }
613 }
614
615 for (OnExit onexit : state.getOnExits()) {
616 writeOnExit(writer, onexit);
617 }
618 writer.writeEndElement();
619 }
620
621
622
623
624
625
626
627
628
629 private static void writeParallel(final XMLStreamWriter writer, final Parallel parallel)
630 throws XMLStreamException {
631
632 writer.writeStartElement(ELEM_PARALLEL);
633 writeTransitionTargetId(writer, parallel);
634
635 writeDatamodel(writer, parallel.getDatamodel());
636 writeHistory(writer, parallel.getHistory());
637 for (OnEntry onentry : parallel.getOnEntries()) {
638 writeOnEntry(writer, onentry);
639 }
640
641 for (Transition t : parallel.getTransitionsList()) {
642 writeTransition(writer, t);
643 }
644
645 for (Invoke inv : parallel.getInvokes()) {
646 writeInvoke(writer, inv);
647 }
648
649 for (EnterableState es : parallel.getChildren()) {
650 if (es instanceof Final) {
651 writeFinal(writer, (Final) es);
652 } else if (es instanceof State) {
653 writeState(writer, (State) es);
654 } else if (es instanceof Parallel) {
655 writeParallel(writer, (Parallel) es);
656 }
657 }
658
659 for (OnExit onexit : parallel.getOnExits()) {
660 writeOnExit(writer, onexit);
661 }
662 writer.writeEndElement();
663 }
664
665
666
667
668
669
670
671
672
673 private static void writeFinal(final XMLStreamWriter writer, final Final end)
674 throws XMLStreamException {
675
676 writer.writeStartElement(ELEM_FINAL);
677 writeTransitionTargetId(writer, end);
678 for (OnEntry onentry : end.getOnEntries()) {
679 writeOnEntry(writer, onentry);
680 }
681 for (OnExit onexit : end.getOnExits()) {
682 writeOnExit(writer, onexit);
683 }
684 writer.writeEndElement();
685 }
686
687
688
689
690
691
692
693
694
695 private static void writeInitial(final XMLStreamWriter writer, final Initial initial)
696 throws XMLStreamException {
697
698 if (initial == null || initial.isGenerated()) {
699 return;
700 }
701
702 writer.writeStartElement(ELEM_INITIAL);
703 writeTransition(writer, initial.getTransition());
704 writer.writeEndElement();
705 }
706
707
708
709
710
711
712
713
714
715
716 private static void writeHistory(final XMLStreamWriter writer, final List<History> history)
717 throws XMLStreamException {
718
719 if (history == null) {
720 return;
721 }
722
723 for (History h : history) {
724 writer.writeStartElement(ELEM_HISTORY);
725 writeTransitionTargetId(writer, h);
726 if (h.isDeep()) {
727 writeAV(writer, ATTR_TYPE, "deep");
728 } else {
729 writeAV(writer, ATTR_TYPE, "shallow");
730 }
731 writeTransition(writer, h.getTransition());
732 writer.writeEndElement();
733 }
734 }
735
736
737
738
739
740
741
742
743
744 private static void writeOnEntry(final XMLStreamWriter writer, final OnEntry onentry)
745 throws XMLStreamException {
746
747 if (onentry != null && (onentry.isRaiseEvent() || onentry.getActions().size() > 0 )) {
748 writer.writeStartElement(ELEM_ONENTRY);
749 writeAV(writer, ATTR_EVENT, onentry.getRaiseEvent());
750 writeExecutableContent(writer, onentry.getActions());
751 writer.writeEndElement();
752 }
753 }
754
755
756
757
758
759
760
761
762
763 private static void writeOnExit(final XMLStreamWriter writer, final OnExit onexit)
764 throws XMLStreamException {
765
766 if (onexit != null && (onexit.isRaiseEvent() || onexit.getActions().size() > 0)) {
767 writer.writeStartElement(ELEM_ONEXIT);
768 writeAV(writer, ATTR_EVENT, onexit.getRaiseEvent());
769 writeExecutableContent(writer, onexit.getActions());
770 writer.writeEndElement();
771 }
772 }
773
774
775
776
777
778
779
780
781
782 private static void writeTransition(final XMLStreamWriter writer, final SimpleTransition transition)
783 throws XMLStreamException {
784
785 writer.writeStartElement(ELEM_TRANSITION);
786 if (transition instanceof Transition) {
787 writeAV(writer, ATTR_EVENT, ((Transition)transition).getEvent());
788 writeAV(writer, ATTR_COND, escapeXML(((Transition)transition).getCond()));
789 }
790
791 writeAV(writer, ATTR_TARGET, transition.getNext());
792 if (transition.getType() != null) {
793 writeAV(writer, ATTR_TYPE, transition.getType().name());
794 }
795 writeExecutableContent(writer, transition.getActions());
796 writer.writeEndElement();
797 }
798
799
800
801
802
803
804
805
806
807 private static void writeInvoke(final XMLStreamWriter writer, final Invoke invoke)
808 throws XMLStreamException {
809
810 writer.writeStartElement(ELEM_INVOKE);
811 writeAV(writer, ATTR_ID, invoke.getId());
812 writeAV(writer, ATTR_SRC, invoke.getSrc());
813 writeAV(writer, ATTR_SRCEXPR, invoke.getSrcexpr());
814 writeAV(writer, ATTR_TYPE, invoke.getType());
815 writeAV(writer, ATTR_AUTOFORWARD, invoke.getAutoForward());
816
817 for (Param p : invoke.getParams()) {
818 writer.writeStartElement(ELEM_PARAM);
819 writeAV(writer, ATTR_NAME, p.getName());
820 writeAV(writer, ATTR_LOCATION, p.getLocation());
821 writeAV(writer, ATTR_EXPR, escapeXML(p.getExpr()));
822 writer.writeEndElement();
823 }
824 writeFinalize(writer, invoke.getFinalize());
825 writeContent(writer, invoke.getContent());
826
827 writer.writeEndElement();
828 }
829
830
831
832
833
834
835
836
837
838 private static void writeFinalize(final XMLStreamWriter writer, final Finalize finalize)
839 throws XMLStreamException {
840
841 if (finalize != null && finalize.getActions().size() > 0) {
842 writer.writeStartElement(ELEM_FINALIZE);
843 writeExecutableContent(writer, finalize.getActions());
844 writer.writeEndElement();
845 }
846 }
847
848
849
850
851
852
853
854
855
856
857 private static void writeExecutableContent(final XMLStreamWriter writer, final List<Action> actions)
858 throws XMLStreamException {
859
860 if (actions == null) {
861 return;
862 }
863 for (Action a : actions) {
864 if (a instanceof Assign) {
865 Assign asn = (Assign) a;
866 writer.writeStartElement(XMLNS_SCXML, ELEM_ASSIGN);
867 writeAV(writer, ATTR_LOCATION, asn.getLocation());
868 if (asn.getType() != null) {
869 writeAV(writer, ATTR_TYPE, asn.getType().value());
870 }
871 writeAV(writer, ATTR_ATTR, asn.getAttr());
872 writeAV(writer, ATTR_SRC, asn.getSrc());
873 writeAV(writer, ATTR_EXPR, escapeXML(asn.getExpr()));
874 writer.writeEndElement();
875 } else if (a instanceof Send) {
876 writeSend(writer, (Send) a);
877 } else if (a instanceof Cancel) {
878 Cancel c = (Cancel) a;
879 writer.writeStartElement(XMLNS_SCXML, ELEM_CANCEL);
880 writeAV(writer, ATTR_SENDID, c.getSendid());
881 writer.writeEndElement();
882 } else if (a instanceof Foreach) {
883 writeForeach(writer, (Foreach) a);
884 } else if (a instanceof Log) {
885 Log lg = (Log) a;
886 writer.writeStartElement(XMLNS_SCXML, ELEM_LOG);
887 writeAV(writer, ATTR_LABEL, lg.getLabel());
888 writeAV(writer, ATTR_EXPR, escapeXML(lg.getExpr()));
889 writer.writeEndElement();
890 } else if (a instanceof Raise) {
891 Raise e = (Raise) a;
892 writer.writeStartElement(XMLNS_SCXML, ELEM_RAISE);
893 writeAV(writer, ATTR_EVENT, e.getEvent());
894 writer.writeEndElement();
895 } else if (a instanceof Script) {
896 Script s = (Script) a;
897 writer.writeStartElement(XMLNS_SCXML, ELEM_SCRIPT);
898 writer.writeCData(s.getScript());
899 writer.writeEndElement();
900 } else if (a instanceof If) {
901 writeIf(writer, (If) a);
902 } else if (a instanceof Else) {
903 writer.writeEmptyElement(ELEM_ELSE);
904 } else if (a instanceof ElseIf) {
905 ElseIf eif = (ElseIf) a;
906 writer.writeStartElement(XMLNS_SCXML, ELEM_ELSEIF);
907 writeAV(writer, ATTR_COND, escapeXML(eif.getCond()));
908 writer.writeEndElement();
909 } else if (a instanceof Var) {
910 Var v = (Var) a;
911 writer.writeStartElement(XMLNS_COMMONS_SCXML, ELEM_VAR);
912 writeAV(writer, ATTR_NAME, v.getName());
913 writeAV(writer, ATTR_EXPR, escapeXML(v.getExpr()));
914 writer.writeEndElement();
915 } else {
916 writer.writeComment("Custom action with class name '" + a.getClass().getName() + "' not serialized");
917 }
918 }
919 }
920
921
922
923
924
925
926
927
928
929 private static void writeSend(final XMLStreamWriter writer, final Send send)
930 throws XMLStreamException {
931
932 writer.writeStartElement(XMLNS_SCXML, ELEM_SEND);
933 writeAV(writer, ATTR_ID, send.getId());
934 writeAV(writer, ATTR_IDLOCATION, send.getIdlocation());
935 writeAV(writer, ATTR_EVENT, send.getEvent());
936 writeAV(writer, ATTR_EVENTEXPR, send.getEventexpr());
937 writeAV(writer, ATTR_TARGET, send.getTarget());
938 writeAV(writer, ATTR_TARGETEXPR, send.getTargetexpr());
939 writeAV(writer, ATTR_TYPE, send.getType());
940 writeAV(writer, ATTR_TYPEEXPR, send.getTypeexpr());
941 writeAV(writer, ATTR_DELAY, send.getDelay());
942 writeAV(writer, ATTR_DELAYEXPR, send.getDelayexpr());
943 writeAV(writer, ATTR_NAMELIST, send.getNamelist());
944 writeAV(writer, ATTR_HINTS, send.getHints());
945
946 for (Param p : send.getParams()) {
947 writer.writeStartElement(ELEM_PARAM);
948 writeAV(writer, ATTR_NAME, p.getName());
949 writeAV(writer, ATTR_LOCATION, p.getLocation());
950 writeAV(writer, ATTR_EXPR, escapeXML(p.getExpr()));
951 writer.writeEndElement();
952 }
953 writeContent(writer, send.getContent());
954
955 writer.writeEndElement();
956 }
957
958
959
960
961
962
963
964
965
966 private static void writeIf(final XMLStreamWriter writer, final If iff)
967 throws XMLStreamException {
968
969 writer.writeStartElement(ELEM_IF);
970 writeAV(writer, ATTR_COND, escapeXML(iff.getCond()));
971 writeExecutableContent(writer, iff.getActions());
972 writer.writeEndElement();
973 }
974
975
976
977
978
979
980
981
982
983 private static void writeForeach(final XMLStreamWriter writer, final Foreach foreach)
984 throws XMLStreamException {
985
986 writer.writeStartElement(ELEM_FOREACH);
987 writeAV(writer, ATTR_ITEM, foreach.getItem());
988 writeAV(writer, ATTR_INDEX, foreach.getIndex());
989 writeAV(writer, ATTR_ARRAY, escapeXML(foreach.getArray()));
990 writeExecutableContent(writer, foreach.getActions());
991 writer.writeEndElement();
992 }
993
994
995
996
997
998
999
1000
1001
1002 private static void writeContent(final XMLStreamWriter writer, final Content content)
1003 throws XMLStreamException {
1004
1005 if (content != null) {
1006 writer.writeStartElement(ELEM_CONTENT);
1007 writeAV(writer, ATTR_EXPR, content.getExpr());
1008 if (content.getBody() != null) {
1009 if (content.getBody() instanceof Node) {
1010 NodeList nodeList = ((Node)content.getBody()).getChildNodes();
1011 if (nodeList.getLength() > 0 && XFORMER == null) {
1012 writer.writeComment("External content was not serialized");
1013 }
1014 else {
1015 for (int i = 0, size = nodeList.getLength(); i < size; i++) {
1016 writeNode(writer, nodeList.item(i));
1017 }
1018 }
1019 }
1020 else {
1021 writer.writeCharacters(content.getBody().toString());
1022 }
1023 }
1024 writer.writeEndElement();
1025 }
1026 }
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036 private static void writeExternalContent(final XMLStreamWriter writer,
1037 final ExternalContent externalContent)
1038 throws XMLStreamException {
1039
1040 List<Node> externalNodes = externalContent.getExternalNodes();
1041
1042 if (externalNodes.size() > 0 && XFORMER == null) {
1043 writer.writeComment("External content was not serialized");
1044 } else {
1045 for (Node n : externalNodes) {
1046 writeNode(writer, n);
1047 }
1048 }
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059 private static void writeNode(final XMLStreamWriter writer, final Node node)
1060 throws XMLStreamException {
1061
1062 Source input = new DOMSource(node);
1063 StringWriter out = new StringWriter();
1064 Result output = new StreamResult(out);
1065 try {
1066 XFORMER.transform(input, output);
1067 } catch (TransformerException te) {
1068 org.apache.commons.logging.Log log = LogFactory.getLog(SCXMLWriter.class);
1069 log.error(te.getMessage(), te);
1070 writer.writeComment("TransformerException: Node was not serialized");
1071 }
1072 writer.writeCharacters(out.toString());
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084 private static void writeAV(final XMLStreamWriter writer, final String localName, final String value)
1085 throws XMLStreamException {
1086 if (value != null) {
1087 writer.writeAttribute(localName, value);
1088 }
1089 }
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100 private static void writeAV(final XMLStreamWriter writer, final String localName, final Boolean value)
1101 throws XMLStreamException {
1102 if (value != null) {
1103 writer.writeAttribute(localName, value.toString());
1104 }
1105 }
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120 private static void writePretty(final Configuration configuration, final OutputStream scxmlStream,
1121 final Writer scxmlWriter, final Result scxmlResult)
1122 throws IOException, XMLStreamException {
1123
1124
1125 configuration.internalWriter.flush();
1126 Source prettyPrintSource = new StreamSource(new StringReader(configuration.internalWriter.toString()));
1127 Result prettyPrintResult = null;
1128 if (scxmlStream != null) {
1129 prettyPrintResult = new StreamResult(scxmlStream);
1130 } else if (scxmlWriter != null) {
1131 prettyPrintResult = new StreamResult(scxmlWriter);
1132 } else if (scxmlResult != null) {
1133 prettyPrintResult = scxmlResult;
1134 }
1135
1136 TransformerFactory factory = TransformerFactory.newInstance();
1137 try {
1138 Transformer transformer = factory.newTransformer();
1139 if (configuration.encoding != null) {
1140 transformer.setOutputProperty(OutputKeys.ENCODING, configuration.encoding);
1141 }
1142 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
1143 transformer.transform(prettyPrintSource, prettyPrintResult);
1144 } catch (TransformerException te) {
1145 throw new XMLStreamException("TransformerException while pretty printing SCXML", te);
1146 }
1147 }
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162 private static XMLStreamWriter getWriter(final Configuration configuration, final OutputStream stream,
1163 final Writer writer, final Result result)
1164 throws XMLStreamException {
1165
1166
1167 XMLOutputFactory factory = XMLOutputFactory.newInstance();
1168
1169
1170
1171
1172
1173
1174 for (Map.Entry<String, Object> property : configuration.properties.entrySet()) {
1175 factory.setProperty(property.getKey(), property.getValue());
1176 }
1177
1178 XMLStreamWriter xsw = null;
1179 if (configuration.usePrettyPrint || configuration.writeToString) {
1180 xsw = factory.createXMLStreamWriter(configuration.internalWriter);
1181 } else if (stream != null) {
1182 if (configuration.encoding != null) {
1183 xsw = factory.createXMLStreamWriter(stream, configuration.encoding);
1184 } else {
1185 xsw = factory.createXMLStreamWriter(stream);
1186 }
1187 } else if (writer != null) {
1188 xsw = factory.createXMLStreamWriter(writer);
1189 } else if (result != null) {
1190 xsw = factory.createXMLStreamWriter(result);
1191 }
1192 return xsw;
1193 }
1194
1195
1196
1197
1198
1199
1200 private static Transformer getTransformer() {
1201 Transformer transformer;
1202 Properties outputProps = new Properties();
1203 outputProps.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
1204 outputProps.put(OutputKeys.STANDALONE, "no");
1205 outputProps.put(OutputKeys.INDENT, "yes");
1206 try {
1207 TransformerFactory tfFactory = TransformerFactory.newInstance();
1208 transformer = tfFactory.newTransformer();
1209 transformer.setOutputProperties(outputProps);
1210 } catch (TransformerFactoryConfigurationError t) {
1211 org.apache.commons.logging.Log log = LogFactory.getLog(SCXMLWriter.class);
1212 log.error(t.getMessage(), t);
1213 return null;
1214 } catch (TransformerConfigurationException e) {
1215 org.apache.commons.logging.Log log = LogFactory.getLog(SCXMLWriter.class);
1216 log.error(e.getMessage(), e);
1217 return null;
1218 }
1219 return transformer;
1220 }
1221
1222
1223
1224
1225 private SCXMLWriter() {
1226 super();
1227 }
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242 public static class Configuration {
1243
1244
1245
1246
1247
1248
1249
1250
1251 final String factoryId;
1252
1253
1254
1255
1256 final ClassLoader factoryClassLoader;
1257
1258
1259
1260
1261
1262 final Map<String, Object> properties;
1263
1264
1265
1266
1267
1268 final String encoding;
1269
1270
1271
1272
1273 final boolean usePrettyPrint;
1274
1275
1276
1277
1278
1279
1280 final Writer internalWriter;
1281
1282
1283
1284
1285
1286 final boolean closeUnderlyingWhenDone;
1287
1288
1289
1290
1291 boolean writeToString;
1292
1293
1294
1295
1296 String prettyPrintOutput;
1297
1298
1299
1300
1301
1302
1303
1304 public Configuration() {
1305
1306 this(null, null, null, null, false, false, false);
1307 }
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325 public Configuration(final String factoryId, final ClassLoader factoryClassLoader,
1326 final Map<String, Object> properties, final String encoding, final boolean usePrettyPrint,
1327 final boolean closeUnderlyingWhenDone) {
1328
1329 this(factoryId, factoryClassLoader, properties, encoding, usePrettyPrint, closeUnderlyingWhenDone, false);
1330 }
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341 Configuration(final boolean writeToString, final boolean usePrettyPrint) {
1342
1343 this(null, null, null, null, usePrettyPrint, false, writeToString);
1344 }
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361 Configuration(final String factoryId, final ClassLoader factoryClassLoader,
1362 final Map<String, Object> properties, final String encoding, final boolean usePrettyPrint,
1363 final boolean closeUnderlyingWhenDone, final boolean writeToString) {
1364
1365 this.factoryId = factoryId;
1366 this.factoryClassLoader = factoryClassLoader;
1367 this.properties = (properties == null ? new HashMap<String, Object>() : properties);
1368 this.encoding = encoding;
1369 this.usePrettyPrint = usePrettyPrint;
1370 this.closeUnderlyingWhenDone = closeUnderlyingWhenDone;
1371 this.writeToString = writeToString;
1372 if (this.usePrettyPrint || this.writeToString) {
1373 this.internalWriter = new StringWriter();
1374 } else {
1375 this.internalWriter = null;
1376 }
1377 }
1378 }
1379 }