    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     *      "<i>cs</i>" prefix</li>
    079     *  <li>All namespace prefixes needed throughout the document are
    080     *      declared on the document root element (<scxml>)</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 <target> 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 <exit/>
    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