View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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   * <p>Utility class for serializing the Commons SCXML Java object
69   * model. Class uses the visitor pattern to trace through the
70   * object heirarchy. Used primarily for testing, debugging and
71   * visual verification.</p>
72   *
73   * <b>NOTE:</b> This serializer makes the following assumptions about the
74   * original SCXML document(s) parsed to create the object model:
75   * <ul>
76   *  <li>The default document namespace is the SCXML namespace:
77   *      <i>http://www.w3.org/2005/07/scxml</i></li>
78   *  <li>The Commons SCXML namespace
79   *      ( <i>http://commons.apache.org/scxml</i> ), if needed, uses the
80   *      &quot;<i>cs</i>&quot; prefix</li>
81   *  <li>All namespace prefixes needed throughout the document are
82   *      declared on the document root element (&lt;scxml&gt;)</li>
83   * </ul>
84   */
85  public class SCXMLSerializer {
86  
87      /** The indent to be used while serializing an SCXML object. */
88      private static final String INDENT = " ";
89      /** The JAXP transformer. */
90      private static final Transformer XFORMER = getTransformer();
91      /** The SCXML namespace. */
92      private static final String NAMESPACE_SCXML =
93          "http://www.w3.org/2005/07/scxml";
94      /** The Commons SCXML namespace. */
95      private static final String NAMESPACE_COMMONS_SCXML =
96          "http://commons.apache.org/scxml";
97  
98      /**
99       * Serialize this SCXML object (primarily for debugging).
100      *
101      * @param scxml
102      *            The SCXML to be serialized
103      * @return String The serialized SCXML
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      * Serialize this State object.
141      *
142      * @param b The buffer to append the serialization to
143      * @param s The State to serialize
144      * @param indent The indent for this XML element
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(); //TODO: Remove in v1.0
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      * Serialize this Parallel object.
196      *
197      * @param b The buffer to append the serialization to
198      * @param p The Parallel to serialize
199      * @param indent The indent for this XML element
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      * Serialize this Invoke object.
218      *
219      * @param b The buffer to append the serialization to
220      * @param i The Invoke to serialize
221      * @param indent The indent for this XML element
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         // Prefer src
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      * Serialize this Initial object.
257      *
258      * @param b The buffer to append the serialization to
259      * @param i The Initial to serialize
260      * @param indent The indent for this XML element
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      * Serialize the History.
273      *
274      * @param b The buffer to append the serialization to
275      * @param l The List of History objects to serialize
276      * @param indent The indent for this XML element
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      * Serialize this Transition object.
299      *
300      * @param b The buffer to append the serialization to
301      * @param t The Transition to serialize
302      * @param indent The indent for this XML element
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      * Serialize this Transition's Target.
328      *
329      *
330      * @param b The buffer to append the serialization to
331      * @param t The Transition whose Target needs to be serialized
332      * @param indent The indent for this XML element
333      *
334      * @deprecated Inline &lt;target&gt; element has been deprecated
335      *             in the SCXML WD
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             // The inline transition target can only be a state
342             serializeState(b, (State) t.getTarget(), indent + INDENT);
343             b.append(indent).append("</target>");
344         }
345     }
346 
347     /**
348      * Serialize this Datamodel object.
349      *
350      * @param b The buffer to append the serialization to
351      * @param dm The Datamodel to be serialized
352      * @param indent The indent for this XML element
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      * Serialize this OnEntry object.
395      *
396      * @param b The buffer to append the serialization to
397      * @param t The TransitionTarget whose OnEntry is to be serialized
398      * @param indent The indent for this XML element
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      * Serialize this OnExit object.
412      *
413      * @param b The buffer to append the serialization to
414      * @param t The TransitionTarget whose OnExit is to be serialized
415      * @param indent The indent for this XML element
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      * Serialize this List of actions.
429      *
430      * @param b The buffer to append the serialization to
431      * @param l The List of actions to serialize
432      * @param indent The indent for this XML element
433      * @return boolean true if the list of actions contains an &lt;exit/&gt;
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      * Serialize this Send object.
508      *
509      * @param b The buffer to append the serialization to
510      * @param send The Send object to serialize
511      * @param indent The indent for this XML element
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      * Return serialized body of <code>ExternalContent</code>.
544      *
545      * @param externalContent The model element containing the body content
546      * @return String The serialized body content
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      * Serialize this If object.
575      *
576      * @param b The buffer to append the serialization to
577      * @param iff The If object to serialize
578      * @param indent The indent for this XML element
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      * Serialize properties of TransitionTarget which are element attributes.
590      *
591      * @param b The buffer to append the serialization to
592      * @param t The TransitionTarget
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      * Serialize namespace declarations for the root SCXML element.
604      *
605      * @param holder The {@link NamespacePrefixesHolder} object
606      * @return The serialized namespace declarations
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      * Get a <code>Transformer</code> instance.
642      *
643      * @return Transformer The <code>Transformer</code> instance.
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      * Private methods.
665      */
666     /**
667      * Discourage instantiation since this is a utility class.
668      */
669     private SCXMLSerializer() {
670         super();
671     }
672 
673 }