001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.scxml.io;
018    
019    import java.io.StringWriter;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Properties;
024    import java.util.Set;
025    
026    import javax.xml.transform.OutputKeys;
027    import javax.xml.transform.Result;
028    import javax.xml.transform.Source;
029    import javax.xml.transform.Transformer;
030    import javax.xml.transform.TransformerException;
031    import javax.xml.transform.TransformerFactory;
032    import javax.xml.transform.dom.DOMSource;
033    import javax.xml.transform.stream.StreamResult;
034    
035    import org.apache.commons.logging.LogFactory;
036    import org.apache.commons.scxml.SCXMLHelper;
037    import org.apache.commons.scxml.model.Action;
038    import org.apache.commons.scxml.model.Assign;
039    import org.apache.commons.scxml.model.Cancel;
040    import org.apache.commons.scxml.model.Data;
041    import org.apache.commons.scxml.model.Datamodel;
042    import org.apache.commons.scxml.model.Else;
043    import org.apache.commons.scxml.model.ElseIf;
044    import org.apache.commons.scxml.model.Exit;
045    import org.apache.commons.scxml.model.ExternalContent;
046    import org.apache.commons.scxml.model.Finalize;
047    import org.apache.commons.scxml.model.History;
048    import org.apache.commons.scxml.model.If;
049    import org.apache.commons.scxml.model.Initial;
050    import org.apache.commons.scxml.model.Invoke;
051    import org.apache.commons.scxml.model.Log;
052    import org.apache.commons.scxml.model.NamespacePrefixesHolder;
053    import org.apache.commons.scxml.model.OnEntry;
054    import org.apache.commons.scxml.model.OnExit;
055    import org.apache.commons.scxml.model.Parallel;
056    import org.apache.commons.scxml.model.Param;
057    import org.apache.commons.scxml.model.SCXML;
058    import org.apache.commons.scxml.model.Send;
059    import org.apache.commons.scxml.model.State;
060    import org.apache.commons.scxml.model.Transition;
061    import org.apache.commons.scxml.model.TransitionTarget;
062    import org.apache.commons.scxml.model.Var;
063    import org.w3c.dom.Node;
064    
065    /**
066     * <p>Utility class for serializing the Commons SCXML Java object
067     * model. Class uses the visitor pattern to trace through the
068     * object heirarchy. Used primarily for testing, debugging and
069     * visual verification.</p>
070     *
071     * <b>NOTE:</b> This serializer makes the following assumptions about the
072     * original SCXML document(s) parsed to create the object model:
073     * <ul>
074     *  <li>The default document namespace is the SCXML namespace:
075     *      <i>http://www.w3.org/2005/07/scxml</i></li>
076     *  <li>The Commons SCXML namespace
077     *      ( <i>http://commons.apache.org/scxml</i> ), if needed, uses the
078     *      &quot;<i>cs</i>&quot; prefix</li>
079     *  <li>All namespace prefixes needed throughout the document are
080     *      declared on the document root element (&lt;scxml&gt;)</li>
081     * </ul>
082     */
083    public class SCXMLSerializer {
084    
085        /** The indent to be used while serializing an SCXML object. */
086        private static final String INDENT = " ";
087        /** The JAXP transformer. */
088        private static final Transformer XFORMER = getTransformer();
089        /** The SCXML namespace. */
090        private static final String NAMESPACE_SCXML =
091            "http://www.w3.org/2005/07/scxml";
092        /** The Commons SCXML namespace. */
093        private static final String NAMESPACE_COMMONS_SCXML =
094            "http://commons.apache.org/scxml";
095    
096        /**
097         * Serialize this SCXML object (primarily for debugging).
098         *
099         * @param scxml
100         *            The SCXML to be serialized
101         * @return String The serialized SCXML
102         */
103        public static String serialize(final SCXML scxml) {
104            StringBuffer b =
105                new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n").
106                    append("<scxml xmlns=\"").append(NAMESPACE_SCXML).
107                    append("\"").append(serializeNamespaceDeclarations(scxml)).
108                    append(" version=\"").append(scxml.getVersion()).
109                    append("\" initial=\"").append(scxml.getInitial()).
110                    append("\">\n");
111            if (XFORMER == null) {
112                org.apache.commons.logging.Log log = LogFactory.
113                    getLog(SCXMLSerializer.class);
114                log.warn("SCXMLSerializer: DOM serialization pertinent to"
115                    + " the document will be skipped since a suitable"
116                    + " JAXP Transformer could not be instantiated.");
117            }
118            b.append(INDENT).append("<!-- http://commons.apache.org/scxml -->\n");
119            Datamodel dm = scxml.getDatamodel();
120            if (dm != null) {
121                serializeDatamodel(b, dm, INDENT);
122            }
123            Map c = scxml.getChildren();
124            Iterator i = c.keySet().iterator();
125            while (i.hasNext()) {
126                TransitionTarget tt = (TransitionTarget) c.get(i.next());
127                if (tt instanceof State) {
128                    serializeState(b, (State) tt, INDENT);
129                } else {
130                    serializeParallel(b, (Parallel) tt, INDENT);
131                }
132            }
133            b.append("</scxml>\n");
134            return b.toString();
135        }
136    
137        /**
138         * Serialize this State object.
139         *
140         * @param b The buffer to append the serialization to
141         * @param s The State to serialize
142         * @param indent The indent for this XML element
143         */
144        public static void serializeState(final StringBuffer b,
145                final State s, final String indent) {
146            b.append(indent).append("<state");
147            serializeTransitionTargetAttributes(b, s);
148            boolean f = s.isFinal();
149            if (f) {
150                b.append(" final=\"true\"");
151            }
152            b.append(">\n");
153            Initial ini = s.getInitial();
154            if (ini != null) {
155                serializeInitial(b, ini, indent + INDENT);
156            }
157            List h = s.getHistory();
158            if (h != null) {
159                serializeHistory(b, h, indent + INDENT);
160            }
161            Datamodel dm = s.getDatamodel();
162            if (dm != null) {
163                serializeDatamodel(b, dm, indent + INDENT);
164            }
165            serializeOnEntry(b, s, indent + INDENT);
166            List t = s.getTransitionsList();
167            for (int i = 0; i < t.size(); i++) {
168                serializeTransition(b, (Transition) t.get(i), indent + INDENT);
169            }
170            Parallel p = s.getParallel(); //TODO: Remove in v1.0
171            Invoke inv = s.getInvoke();
172            if (p != null) {
173                serializeParallel(b, p, indent + INDENT);
174            } else if (inv != null) {
175                serializeInvoke(b , inv, indent + INDENT);
176            } else {
177                Map c = s.getChildren();
178                Iterator j = c.keySet().iterator();
179                while (j.hasNext()) {
180                    TransitionTarget tt = (TransitionTarget) c.get(j.next());
181                    if (tt instanceof State) {
182                        serializeState(b, (State) tt, indent + INDENT);
183                    } else if (tt instanceof Parallel) {
184                        serializeParallel(b, (Parallel) tt, indent + INDENT);
185                    }
186                }
187            }
188            serializeOnExit(b, s, indent + INDENT);
189            b.append(indent).append("</state>\n");
190        }
191    
192        /**
193         * Serialize this Parallel object.
194         *
195         * @param b The buffer to append the serialization to
196         * @param p The Parallel to serialize
197         * @param indent The indent for this XML element
198         */
199        public static void serializeParallel(final StringBuffer b,
200                final Parallel p, final String indent) {
201            b.append(indent).append("<parallel");
202            serializeTransitionTargetAttributes(b, p);
203            b.append(">\n");
204            serializeOnEntry(b, p, indent + INDENT);
205            Set s = p.getChildren();
206            Iterator i = s.iterator();
207            while (i.hasNext()) {
208                serializeState(b, (State) i.next(), indent + INDENT);
209            }
210            serializeOnExit(b, p, indent + INDENT);
211            b.append(indent).append("</parallel>\n");
212        }
213    
214        /**
215         * Serialize this Invoke object.
216         *
217         * @param b The buffer to append the serialization to
218         * @param i The Invoke to serialize
219         * @param indent The indent for this XML element
220         */
221        public static void serializeInvoke(final StringBuffer b,
222                final Invoke i, final String indent) {
223            b.append(indent).append("<invoke");
224            String ttype = i.getTargettype();
225            String src = i.getSrc();
226            String srcexpr = i.getSrcexpr();
227            if (ttype != null) {
228                b.append(" targettype=\"").append(ttype).append("\"");
229            }
230            // Prefer src
231            if (src != null) {
232                b.append(" src=\"").append(src).append("\"");
233            } else if (srcexpr != null) {
234                b.append(" srcexpr=\"").append(srcexpr).append("\"");
235            }
236            b.append(">\n");
237            List params = i.params();
238            for (Iterator iter = params.iterator(); iter.hasNext();) {
239                Param p = (Param) iter.next();
240                b.append(indent).append(INDENT).append("<param name=\"").
241                    append(p.getName()).append("\" expr=\"").
242                    append(SCXMLHelper.escapeXML(p.getExpr())).append("\"/>\n");
243            }
244            Finalize f = i.getFinalize();
245            if (f != null) {
246                b.append(indent).append(INDENT).append("<finalize>\n");
247                serializeActions(b, f.getActions(), indent + INDENT + INDENT);
248                b.append(indent).append(INDENT).append("</finalize>\n");
249            }
250            b.append(indent).append("</invoke>\n");
251        }
252    
253        /**
254         * Serialize this Initial object.
255         *
256         * @param b The buffer to append the serialization to
257         * @param i The Initial to serialize
258         * @param indent The indent for this XML element
259         */
260        public static void serializeInitial(final StringBuffer b, final Initial i,
261                final String indent) {
262            b.append(indent).append("<initial");
263            serializeTransitionTargetAttributes(b, i);
264            b.append(">\n");
265            serializeTransition(b, i.getTransition(), indent + INDENT);
266            b.append(indent).append("</initial>\n");
267        }
268    
269        /**
270         * Serialize the History.
271         *
272         * @param b The buffer to append the serialization to
273         * @param l The List of History objects to serialize
274         * @param indent The indent for this XML element
275         */
276        public static void serializeHistory(final StringBuffer b, final List l,
277                final String indent) {
278            if (l.size() > 0) {
279                for (int i = 0; i < l.size(); i++) {
280                    History h = (History) l.get(i);
281                    b.append(indent).append("<history");
282                    serializeTransitionTargetAttributes(b, h);
283                     if (h.isDeep()) {
284                         b.append(" type=\"deep\"");
285                     } else {
286                         b.append(" type=\"shallow\"");
287                     }
288                    b.append(">\n");
289                    serializeTransition(b, h.getTransition(), indent + INDENT);
290                    b.append(indent).append("</history>\n");
291                }
292            }
293        }
294    
295        /**
296         * Serialize this Transition object.
297         *
298         * @param b The buffer to append the serialization to
299         * @param t The Transition to serialize
300         * @param indent The indent for this XML element
301         */
302        public static void serializeTransition(final StringBuffer b,
303                final Transition t, final String indent) {
304            b.append(indent).append("<transition");
305            if (!SCXMLHelper.isStringEmpty(t.getEvent())) {
306                b.append(" event=\"").append(t.getEvent()).append("\"");
307            }
308            if (!SCXMLHelper.isStringEmpty(t.getCond())) {
309                b.append(" cond=\"").append(SCXMLHelper.escapeXML(t.getCond())).
310                    append("\"");
311            }
312            boolean next = !SCXMLHelper.isStringEmpty(t.getNext());
313            if (next) {
314                b.append(" target=\"" + t.getNext() + "\"");
315            }
316            b.append(">\n");
317            boolean exit = serializeActions(b, t.getActions(), indent + INDENT);
318            if (!next && !exit) {
319                serializeTarget(b, t, indent + INDENT);
320            }
321            b.append(indent).append("</transition>\n");
322        }
323    
324        /**
325         * Serialize this Transition's Target.
326         *
327         *
328         * @param b The buffer to append the serialization to
329         * @param t The Transition whose Target needs to be serialized
330         * @param indent The indent for this XML element
331         *
332         * @deprecated Inline &lt;target&gt; element has been deprecated
333         *             in the SCXML WD
334         */
335        public static void serializeTarget(final StringBuffer b,
336                final Transition t, final String indent) {
337            if (t.getTarget() != null) {
338                b.append(indent).append("<target>");
339                // The inline transition target can only be a state
340                serializeState(b, (State) t.getTarget(), indent + INDENT);
341                b.append(indent).append("</target>");
342            }
343        }
344    
345        /**
346         * Serialize this Datamodel object.
347         *
348         * @param b The buffer to append the serialization to
349         * @param dm The Datamodel to be serialized
350         * @param indent The indent for this XML element
351         */
352        public static void serializeDatamodel(final StringBuffer b,
353                final Datamodel dm, final String indent) {
354            List data = dm.getData();
355            if (data != null && data.size() > 0) {
356                b.append(indent).append("<datamodel>\n");
357                if (XFORMER == null) {
358                    b.append(indent).append(INDENT).
359                        append("<!-- Body content was not serialized -->\n");
360                    b.append(indent).append("</datamodel>\n");
361                    return;
362                }
363                for (Iterator iter = data.iterator(); iter.hasNext();) {
364                    Data datum = (Data) iter.next();
365                    Node dataNode = datum.getNode();
366                    if (dataNode != null) {
367                        StringWriter out = new StringWriter();
368                        try {
369                            Source input = new DOMSource(dataNode);
370                            Result output = new StreamResult(out);
371                            XFORMER.transform(input, output);
372                        } catch (TransformerException te) {
373                            org.apache.commons.logging.Log log = LogFactory.
374                                getLog(SCXMLSerializer.class);
375                            log.error(te.getMessage(), te);
376                            b.append(indent).append(INDENT).
377                                append("<!-- Data content not serialized -->\n");
378                        }
379                        b.append(indent).append(INDENT).append(out.toString());
380                    } else {
381                        b.append(indent).append(INDENT).append("<data id=\"").
382                            append(datum.getId()).append("\" expr=\"").
383                            append(SCXMLHelper.escapeXML(datum.getExpr())).
384                            append("\" />\n");
385                    }
386                }
387                b.append(indent).append("</datamodel>\n");
388            }
389        }
390    
391        /**
392         * Serialize this OnEntry object.
393         *
394         * @param b The buffer to append the serialization to
395         * @param t The TransitionTarget whose OnEntry is to be serialized
396         * @param indent The indent for this XML element
397         */
398        public static void serializeOnEntry(final StringBuffer b,
399                final TransitionTarget t, final String indent) {
400            OnEntry e = t.getOnEntry();
401            if (e != null && e.getActions().size() > 0) {
402                b.append(indent).append("<onentry>\n");
403                serializeActions(b, e.getActions(), indent + INDENT);
404                b.append(indent).append("</onentry>\n");
405            }
406        }
407    
408        /**
409         * Serialize this OnExit object.
410         *
411         * @param b The buffer to append the serialization to
412         * @param t The TransitionTarget whose OnExit is to be serialized
413         * @param indent The indent for this XML element
414         */
415        public static void serializeOnExit(final StringBuffer b,
416                final TransitionTarget t, final String indent) {
417            OnExit x = t.getOnExit();
418            if (x != null && x.getActions().size() > 0) {
419                b.append(indent).append("<onexit>\n");
420                serializeActions(b, x.getActions(), indent + INDENT);
421                b.append(indent).append("</onexit>\n");
422            }
423        }
424    
425        /**
426         * Serialize this List of actions.
427         *
428         * @param b The buffer to append the serialization to
429         * @param l The List of actions to serialize
430         * @param indent The indent for this XML element
431         * @return boolean true if the list of actions contains an &lt;exit/&gt;
432         */
433        public static boolean serializeActions(final StringBuffer b, final List l,
434                final String indent) {
435            if (l == null) {
436                return false;
437            }
438            boolean exit = false;
439            Iterator i = l.iterator();
440            while (i.hasNext()) {
441                Action a = (Action) i.next();
442                if (a instanceof Var) {
443                    Var v = (Var) a;
444                    b.append(indent).append("<cs:var name=\"").append(v.getName())
445                        .append("\" expr=\"")
446                        .append(SCXMLHelper.escapeXML(v.getExpr()))
447                        .append("\"/>\n");
448                } else if (a instanceof Assign) {
449                    Assign asn = (Assign) a;
450                    b.append(indent).append("<assign");
451                    if (!SCXMLHelper.isStringEmpty(asn.getLocation())) {
452                        b.append(" location=\"").append(asn.getLocation());
453                        if (!SCXMLHelper.isStringEmpty(asn.getSrc())) {
454                            b.append("\" src=\"").append(asn.getSrc());
455                        } else {
456                            b.append("\" expr=\"").
457                                append(SCXMLHelper.escapeXML(asn.getExpr()));
458                        }
459                    } else {
460                        b.append(" name=\"").append(asn.getName()).
461                            append("\" expr=\"").
462                            append(SCXMLHelper.escapeXML(asn.getExpr()));
463                    }
464                    b.append("\"/>\n");
465                } else if (a instanceof Send) {
466                    serializeSend(b, (Send) a, indent);
467                } else if (a instanceof Cancel) {
468                    Cancel c = (Cancel) a;
469                    b.append(indent).append("<cancel sendid=\"")
470                        .append(c.getSendid()).append("\"/>\n");
471                } else if (a instanceof Log) {
472                    Log lg = (Log) a;
473                    b.append(indent).append("<log expr=\"").
474                        append(SCXMLHelper.escapeXML(lg.getExpr())).
475                        append("\"/>\n");
476                } else if (a instanceof Exit) {
477                    Exit e = (Exit) a;
478                    b.append(indent).append("<cs:exit");
479                    String expr = SCXMLHelper.escapeXML(e.getExpr());
480                    String nl = e.getNamelist();
481                    if (expr != null) {
482                        b.append(" expr=\"" + expr + "\"");
483                    }
484                    if (nl != null) {
485                        b.append(" namelist=\"" + nl + "\"");
486                    }
487                    b.append("/>\n");
488                    exit = true;
489                } else if (a instanceof If) {
490                    If iff = (If) a;
491                    serializeIf(b, iff, indent);
492                } else if (a instanceof Else) {
493                    b.append(indent).append("<else/>\n");
494                } else if (a instanceof ElseIf) {
495                    ElseIf eif = (ElseIf) a;
496                    b.append(indent).append("<elseif cond=\"")
497                        .append(SCXMLHelper.escapeXML(eif.getCond()))
498                        .append("\" />\n");
499                }
500            }
501            return exit;
502        }
503    
504        /**
505         * Serialize this Send object.
506         *
507         * @param b The buffer to append the serialization to
508         * @param send The Send object to serialize
509         * @param indent The indent for this XML element
510         */
511        public static void serializeSend(final StringBuffer b,
512                final Send send, final String indent) {
513            b.append(indent).append("<send");
514            if (send.getSendid() != null) {
515                b.append(" sendid=\"").append(send.getSendid()).append("\"");
516            }
517            if (send.getTarget() != null) {
518                b.append(" target=\"").append(send.getTarget()).append("\"");
519            }
520            if (send.getTargettype() != null) {
521                b.append(" targetType=\"").append(send.getTargettype()).append("\"");
522            }
523            if (send.getNamelist() != null) {
524                b.append(" namelist=\"").append(send.getNamelist()).append("\"");
525            }
526            if (send.getDelay() != null) {
527                b.append(" delay=\"").append(send.getDelay()).append("\"");
528            }
529            if (send.getEvent() != null) {
530                b.append(" event=\"").append(send.getEvent()).append("\"");
531            }
532            if (send.getHints() != null) {
533                b.append(" hints=\"").append(send.getHints()).append("\"");
534            }
535            b.append(">\n");
536            b.append(getBodyContent(send));
537            b.append(indent).append("</send>\n");
538        }
539    
540        /**
541         * Return serialized body of <code>ExternalContent</code>.
542         *
543         * @param externalContent The model element containing the body content
544         * @return String The serialized body content
545         */
546        public static final String getBodyContent(
547                final ExternalContent externalContent) {
548            StringBuffer buf = new StringBuffer();
549            List externalNodes = externalContent.getExternalNodes();
550            if (externalNodes.size() > 0 && XFORMER == null) {
551                buf.append("<!-- Body content was not serialized -->\n");
552                return buf.toString();
553            }
554            for (int i = 0; i < externalNodes.size(); i++) {
555                Source input = new DOMSource((Node) externalNodes.get(i));
556                StringWriter out = new StringWriter();
557                Result output = new StreamResult(out);
558                try {
559                    XFORMER.transform(input, output);
560                } catch (TransformerException te) {
561                    org.apache.commons.logging.Log log = LogFactory.
562                        getLog(SCXMLSerializer.class);
563                    log.error(te.getMessage(), te);
564                    buf.append("<!-- Not all body content was serialized -->");
565                }
566                buf.append(out.toString()).append("\n");
567            }
568            return buf.toString();
569        }
570    
571        /**
572         * Serialize this If object.
573         *
574         * @param b The buffer to append the serialization to
575         * @param iff The If object to serialize
576         * @param indent The indent for this XML element
577         */
578        public static void serializeIf(final StringBuffer b,
579                final If iff, final String indent) {
580            b.append(indent).append("<if cond=\"").append(SCXMLHelper.
581                escapeXML(iff.getCond())).append("\">\n");
582            serializeActions(b, iff.getActions(), indent + INDENT);
583            b.append(indent).append("</if>\n");
584        }
585    
586        /**
587         * Serialize properties of TransitionTarget which are element attributes.
588         *
589         * @param b The buffer to append the serialization to
590         * @param t The TransitionTarget
591         */
592        private static void serializeTransitionTargetAttributes(
593                final StringBuffer b, final TransitionTarget t) {
594            String id = t.getId();
595            if (id != null) {
596                b.append(" id=\"").append(id).append("\"");
597            }
598        }
599    
600        /**
601         * Serialize namespace declarations for the root SCXML element.
602         *
603         * @param holder The {@link NamespacePrefixesHolder} object
604         * @return The serialized namespace declarations
605         */
606        private static String serializeNamespaceDeclarations(
607                final NamespacePrefixesHolder holder) {
608            Map ns = holder.getNamespaces();
609            StringBuffer b = new StringBuffer();
610            if (ns != null) {
611                Iterator iter = ns.entrySet().iterator();
612                while (iter.hasNext()) {
613                    Map.Entry entry = (Map.Entry) iter.next();
614                    String prefix = (String) entry.getKey();
615                    String nsURI = (String) entry.getValue();
616                    if (prefix.length() == 0 && !nsURI.equals(NAMESPACE_SCXML)) {
617                        org.apache.commons.logging.Log log = LogFactory.
618                            getLog(SCXMLSerializer.class);
619                        log.warn("When using the SCXMLSerializer, the default "
620                            + "namespace must be the SCXML namespace:"
621                            + NAMESPACE_SCXML);
622                    } if (prefix.equals("cs")
623                            && !nsURI.equals(NAMESPACE_COMMONS_SCXML)) {
624                        org.apache.commons.logging.Log log = LogFactory.
625                            getLog(SCXMLSerializer.class);
626                        log.warn("When using the SCXMLSerializer, the namespace"
627                            + "prefix \"cs\" must bind to the Commons SCXML "
628                            + "namespace:" + NAMESPACE_COMMONS_SCXML);
629                    } else if (prefix.length() > 0) {
630                        b.append(" xmlns:").append(prefix).append("=\"").
631                            append(nsURI).append("\"");
632                    }
633                }
634            }
635            return b.toString();
636        }
637    
638        /**
639         * Get a <code>Transformer</code> instance.
640         *
641         * @return Transformer The <code>Transformer</code> instance.
642         */
643        private static Transformer getTransformer() {
644            Transformer transformer = null;
645            Properties outputProps = new Properties();
646            outputProps.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
647            outputProps.put(OutputKeys.STANDALONE, "no");
648            outputProps.put(OutputKeys.INDENT, "yes");
649            try {
650                TransformerFactory tfFactory = TransformerFactory.newInstance();
651                transformer = tfFactory.newTransformer();
652                transformer.setOutputProperties(outputProps);
653            } catch (Throwable t) {
654                return null;
655            }
656            return transformer;
657        }
658    
659        /*
660         * Private methods.
661         */
662        /**
663         * Discourage instantiation since this is a utility class.
664         */
665        private SCXMLSerializer() {
666            super();
667        }
668    
669    }
670