    001    /* ========================================================================
    002     * JCommon : a free general purpose class library for the Java(tm) platform
    003     * ========================================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jcommon/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ----------------
    028     * ModelWriter.java
    029     * ----------------
    030     * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
    031     *
    032     * Original Author:  Thomas Morgner;
    033     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    034     *
    035     * $Id: ModelWriter.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
    036     *
    037     * Changes
    038     * -------------------------
    039     * 21.06.2003 : Initial version
    040     *
    041     */
    042
    043    package org.jfree.xml.generator;
    044
    045    import java.io.IOException;
    046    import java.io.Writer;
    047
    048    import org.jfree.xml.generator.model.ClassDescription;
    049    import org.jfree.xml.generator.model.Comments;
    050    import org.jfree.xml.generator.model.DescriptionModel;
    051    import org.jfree.xml.generator.model.IgnoredPropertyInfo;
    052    import org.jfree.xml.generator.model.ManualMappingInfo;
    053    import org.jfree.xml.generator.model.MultiplexMappingInfo;
    054    import org.jfree.xml.generator.model.PropertyInfo;
    055    import org.jfree.xml.generator.model.PropertyType;
    056    import org.jfree.xml.generator.model.TypeInfo;
    057    import org.jfree.xml.util.ClassModelTags;
    058    import org.jfree.xml.writer.AttributeList;
    059    import org.jfree.xml.writer.SafeTagList;
    060    import org.jfree.xml.writer.XMLWriterSupport;
    061
    062    /**
    063     * A model writer.
    064     */
    065    public class ModelWriter {
    066
    067        /** The tags that can be split. */
    068        private static SafeTagList safeTags;
    069
    070        /**
    071         * Returns the safe tag list.
    072         *
    073         * @return The safe tag list.
    074         */
    075        public static SafeTagList getSafeTags() {
    076            if (safeTags == null) {
    077                safeTags = new SafeTagList();
    078                safeTags.add(ClassModelTags.OBJECTS_TAG);
    079                safeTags.add(ClassModelTags.OBJECT_TAG);
    080                safeTags.add(ClassModelTags.CONSTRUCTOR_TAG);
    081                safeTags.add(ClassModelTags.ELEMENT_PROPERTY_TAG);
    082                safeTags.add(ClassModelTags.LOOKUP_PROPERTY_TAG);
    083                safeTags.add(ClassModelTags.ATTRIBUTE_PROPERTY_TAG);
    084                safeTags.add(ClassModelTags.PARAMETER_TAG);
    085                safeTags.add(ClassModelTags.INCLUDE_TAG);
    086                safeTags.add(ClassModelTags.IGNORED_PROPERTY_TAG);
    087                safeTags.add(ClassModelTags.MANUAL_TAG);
    088                safeTags.add(ClassModelTags.MAPPING_TAG);
    089                safeTags.add(ClassModelTags.TYPE_TAG);
    090            }
    091            return safeTags;
    092        }
    093
    094        /** A support class for writing XML tags. */
    095        private XMLWriterSupport writerSupport;
    096
    097        /** A model containing class descriptions. */
    098        private DescriptionModel model;
    099
    100        /**
    101         * Creates a new model writer instance.
    102         */
    103        public ModelWriter() {
    104            this.writerSupport = new XMLWriterSupport(getSafeTags(), 0);
    105        }
    106
    107        /**
    108         * Returns the model.
    109         *
    110         * @return The model.
    111         */
    112        public DescriptionModel getModel() {
    113            return this.model;
    114        }
    115
    116        /**
    117         * Sets the model to be written.
    118         *
    119         * @param model  the model.
    120         */
    121        public void setModel(final DescriptionModel model) {
    122            this.model = model;
    123        }
    124
    125        /**
    126         * Writes an XML header.
    127         *
    128         * @param writer  the writer.
    129         *
    130         * @throws IOException if there is an I/O problem.
    131         */
    132        public static void writeXMLHeader(final Writer writer) throws IOException {
    133            writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
    134            writer.write(XMLWriterSupport.getLineSeparator());
    135        }
    136
    137        /**
    138         * Writes a set of comments.
    139         *
    140         * @param writer  the writer.
    141         * @param comments  a set of comments.
    142         *
    143         * @throws IOException if there is an I/O problem.
    144         */
    145        protected void writeStandardComment(final Writer writer, final Comments comments) throws IOException {
    146            if ((comments == null) || (comments.getOpenTagComment() == null)) {
    147                writer.write(
    148                    "<!-- CVSTag: $Id: ModelWriter.java,v 1.3 2005/10/18 13:32:20 mungady Exp $ "
    149                    + comments + " -->"
    150                );
    151                writer.write(XMLWriterSupport.getLineSeparator());
    152            }
    153            else {
    154                writeComment(writer, comments.getOpenTagComment());
    155            }
    156        }
    157
    158        /**
    159         * Writes a sequence of comments.
    160         *
    161         * @param writer  the writer.
    162         * @param comments  the comments (<code>null</code> ignored).
    163         *
    164         * @throws IOException if there is an I/O problem.
    165         */
    166        protected void writeComment(final Writer writer, final String[] comments) throws IOException {
    167            if (comments == null) {
    168                return;
    169            }
    170            for (int i = 0; i < comments.length; i++) {
    171                this.writerSupport.indent(writer, XMLWriterSupport.INDENT_ONLY);
    172                writer.write("<!--");
    173                writer.write(comments[i]);
    174                writer.write("-->");
    175                writer.write(XMLWriterSupport.getLineSeparator());
    176            }
    177        }
    178
    179        /**
    180         * Writes the open comments from a set of comments.
    181         *
    182         * @param writer  the writer.
    183         * @param comments  the set of comments.
    184         *
    185         * @throws IOException if there is an I/O problem.
    186         */
    187        protected void writeOpenComment(final Writer writer, final Comments comments) throws IOException {
    188            if (comments == null) {
    189                return;
    190            }
    191            if (comments.getOpenTagComment() == null) {
    192                return;
    193            }
    194            writeComment(writer, comments.getOpenTagComment());
    195        }
    196
    197        /**
    198         * Writes the close comments from a set of comments.
    199         *
    200         * @param writer  the writer.
    201         * @param comments  the set of comments.
    202         *
    203         * @throws IOException if there is an I/O problem.
    204         */
    205        protected void writeCloseComment(final Writer writer, final Comments comments) throws IOException {
    206            if (comments == null) {
    207                return;
    208            }
    209            if (comments.getCloseTagComment() == null) {
    210                return;
    211            }
    212            writeComment(writer, comments.getCloseTagComment());
    213        }
    214
    215        /**
    216         * Writes a closed (short) tag with eventually nested comments.
    217         *
    218         * @param writer  the writer.
    219         * @param tagName  the tag name.
    220         * @param attributes  the attributes.
    221         * @param comments  the comments.
    222         *
    223         * @throws IOException if there is an I/O problem.
    224         */
    225        protected void writeTag(final Writer writer,
    226                                final String tagName,
    227                                final AttributeList attributes,
    228                                final Comments comments) throws IOException {
    229            if (comments == null) {
    230                this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.CLOSE);
    231            }
    232            else {
    233                writeOpenComment(writer, comments);
    234                if (comments.getCloseTagComment() != null) {
    235                    this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.OPEN);
    236                    writeCloseComment(writer, comments);
    237                    this.writerSupport.writeCloseTag(writer, tagName);
    238                }
    239                else {
    240                    this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.CLOSE);
    241                }
    242            }
    243        }
    244
    245        /**
    246         * Writes a closed (short) tag with eventually nested comments.
    247         *
    248         * @param writer  the writer.
    249         * @param tagName  the tag name.
    250         * @param attribute  the attribute name.
    251         * @param value  the attribute value.
    252         * @param comments  the comments.
    253         *
    254         * @throws IOException if there is an I/O problem.
    255         */
    256        protected void writeTag(final Writer writer,
    257                                final String tagName,
    258                                final String attribute,
    259                                final String value,
    260                                final Comments comments) throws IOException {
    261            if (comments == null) {
    262                this.writerSupport.writeTag(writer, tagName, attribute, value , XMLWriterSupport.CLOSE);
    263            }
    264            else {
    265                writeOpenComment(writer, comments);
    266                if (comments.getCloseTagComment() != null) {
    267                    this.writerSupport.writeTag(
    268                        writer, tagName, attribute, value, XMLWriterSupport.OPEN
    269                    );
    270                    writeCloseComment(writer, comments);
    271                    this.writerSupport.writeCloseTag(writer, tagName);
    272                }
    273                else {
    274                    this.writerSupport.writeTag(
    275                        writer, tagName, attribute, value, XMLWriterSupport.CLOSE
    276                    );
    277                }
    278            }
    279        }
    280
    281        /**
    282         * Writes a model to the specified writer.
    283         *
    284         * @param writer  the writer.
    285         *
    286         * @throws IOException if there is an I/O problem.
    287         */
    288        public void write(final Writer writer) throws IOException {
    289
    290            writeStandardComment(writer, this.model.getModelComments());
    291            this.writerSupport.writeTag(writer, ClassModelTags.OBJECTS_TAG);
    292            final String[] sources = this.model.getSources();
    293            for (int i = 0; i < sources.length; i++) {
    294                final Comments comments = this.model.getIncludeComment(sources[i]);
    295                writeTag(
    296                    writer,
    297                    ClassModelTags.INCLUDE_TAG, ClassModelTags.SOURCE_ATTR, sources[i], comments
    298                );
    299            }
    300
    301            for (int i = 0; i < this.model.size(); i++) {
    302                final ClassDescription cd = this.model.get(i);
    303                writeClassDescription(writer, cd);
    304            }
    305
    306            final ManualMappingInfo[] mappings = getModel().getMappingModel().getManualMapping();
    307            for (int i = 0; i < mappings.length; i++) {
    308                final ManualMappingInfo mi = mappings[i];
    309                writeManualMapping(writer, mi);
    310            }
    311
    312            final MultiplexMappingInfo[] mmappings = getModel().getMappingModel().getMultiplexMapping();
    313            for (int i = 0; i < mmappings.length; i++) {
    314                final MultiplexMappingInfo mi = mmappings[i];
    315                writeMultiplexMapping(writer, mi);
    316            }
    317
    318            writeCloseComment(writer, this.model.getModelComments());
    319            this.writerSupport.writeCloseTag(writer, ClassModelTags.OBJECTS_TAG);
    320
    321        }
    322
    323        /**
    324         * Writes a manual mapping to the XML output.
    325         *
    326         * @param writer  the writer.
    327         * @param mi  the mapping info.
    328         *
    329         * @throws IOException if there is an I/O problem.
    330         */
    331        protected void writeManualMapping(final Writer writer, final ManualMappingInfo mi) throws IOException {
    332            final AttributeList al = new AttributeList();
    333            al.setAttribute(ClassModelTags.CLASS_ATTR, mi.getBaseClass().getName());
    334            al.setAttribute(ClassModelTags.READ_HANDLER_ATTR, mi.getReadHandler().getName());
    335            al.setAttribute(ClassModelTags.WRITE_HANDLER_ATTR, mi.getWriteHandler().getName());
    336            writeTag(writer, ClassModelTags.MANUAL_TAG, al, mi.getComments());
    337        }
    338
    339        /**
    340         * Writes a multiplex mapping to the XML output.
    341         *
    342         * @param writer  the writer.
    343         * @param mi  the mapping info.
    344         *
    345         * @throws IOException if there is an I/O problem.
    346         */
    347        protected void writeMultiplexMapping(final Writer writer, final MultiplexMappingInfo mi)
    348            throws IOException {
    349
    350            final TypeInfo[] tis = mi.getChildClasses();
    351
    352            final AttributeList al = new AttributeList();
    353            al.setAttribute(ClassModelTags.BASE_CLASS_ATTR, mi.getBaseClass().getName());
    354            al.setAttribute(ClassModelTags.TYPE_ATTR, mi.getTypeAttribute());
    355            getWriterSupport().writeTag(writer, ClassModelTags.MAPPING_TAG, al, XMLWriterSupport.OPEN);
    356
    357            for (int j = 0; j < tis.length; j++) {
    358                final AttributeList tiAttr = new AttributeList();
    359                tiAttr.setAttribute(ClassModelTags.NAME_ATTR, tis[j].getName());
    360                tiAttr.setAttribute(ClassModelTags.CLASS_ATTR, tis[j].getType().getName());
    361                writeTag(writer, ClassModelTags.TYPE_TAG, tiAttr, tis[j].getComments());
    362            }
    363
    364            getWriterSupport().writeCloseTag(writer, ClassModelTags.MAPPING_TAG);
    365        }
    366
    367        /**
    368         * Writes a class description.
    369         *
    370         * @param writer  the writer.
    371         * @param cd  the class description.
    372         *
    373         * @throws IOException if there is an I/O problem.
    374         */
    375        protected void writeClassDescription(final Writer writer, final ClassDescription cd) throws IOException {
    376
    377            if (cd.isUndefined()) {
    378                return;
    379            }
    380
    381            final AttributeList al = new AttributeList();
    382            al.setAttribute(ClassModelTags.CLASS_ATTR, cd.getName());
    383            if (cd.getRegisterKey() != null) {
    384                al.setAttribute(ClassModelTags.REGISTER_NAMES_ATTR, cd.getRegisterKey());
    385            }
    386            if (cd.isPreserve()) {
    387                al.setAttribute(ClassModelTags.IGNORE_ATTR, "true");
    388            }
    389            this.writerSupport.writeTag(writer, ClassModelTags.OBJECT_TAG, al, XMLWriterSupport.OPEN);
    390
    391            final TypeInfo[] constructorInfo = cd.getConstructorDescription();
    392            if (constructorInfo != null && constructorInfo.length != 0) {
    393                this.writerSupport.writeTag(writer, ClassModelTags.CONSTRUCTOR_TAG);
    394                for (int i = 0; i < constructorInfo.length; i++) {
    395                    final AttributeList constructorList = new AttributeList();
    396                    constructorList.setAttribute(
    397                        ClassModelTags.CLASS_ATTR, constructorInfo[i].getType().getName()
    398                    );
    399                    constructorList.setAttribute(
    400                        ClassModelTags.PROPERTY_ATTR, constructorInfo[i].getName()
    401                    );
    402                    writeTag(writer, ClassModelTags.PARAMETER_TAG, constructorList,
    403                             constructorInfo[i].getComments());
    404                }
    405                this.writerSupport.writeCloseTag(writer, ClassModelTags.CONSTRUCTOR_TAG);
    406            }
    407
    408            final PropertyInfo[] properties = cd.getProperties();
    409            for (int i = 0; i < properties.length; i++) {
    410                writePropertyInfo(writer, properties[i]);
    411            }
    412
    413            this.writerSupport.writeCloseTag(writer, ClassModelTags.OBJECT_TAG);
    414        }
    415
    416        /**
    417         * Writes a property info element.
    418         *
    419         * @param writer  the writer.
    420         * @param ipi  the property info.
    421         *
    422         * @throws IOException if there is an I/O problem.
    423         */
    424        private void writePropertyInfo(final Writer writer, final PropertyInfo ipi) throws IOException {
    425            final AttributeList props = new AttributeList();
    426            props.setAttribute(ClassModelTags.NAME_ATTR, ipi.getName());
    427
    428            if (ipi instanceof IgnoredPropertyInfo) {
    429                writeTag(writer, ClassModelTags.IGNORED_PROPERTY_TAG, props, ipi.getComments());
    430                return;
    431            }
    432
    433            if (ipi.getPropertyType().equals(PropertyType.ATTRIBUTE)) {
    434                props.setAttribute(ClassModelTags.ATTRIBUTE_ATTR, ipi.getXmlName());
    435                props.setAttribute(ClassModelTags.ATTRIBUTE_HANDLER_ATTR, ipi.getXmlHandler());
    436                writeTag(writer, ClassModelTags.ATTRIBUTE_PROPERTY_TAG, props, ipi.getComments());
    437            }
    438            else if (ipi.getPropertyType().equals(PropertyType.ELEMENT)) {
    439                if (ipi.getComments() == null || ipi.getComments().getOpenTagComment() == null)
    440                {
    441                    this.writerSupport.indent(writer, XMLWriterSupport.INDENT_ONLY);
    442                    writer.write("<!-- property type is " + ipi.getType() + " -->");
    443                    writer.write(System.getProperty("line.separator", "\n"));
    444                }
    445                props.setAttribute(ClassModelTags.ELEMENT_ATTR, ipi.getXmlName());
    446                writeTag(writer, ClassModelTags.ELEMENT_PROPERTY_TAG, props, ipi.getComments());
    447            }
    448            else {
    449                props.setAttribute(ClassModelTags.LOOKUP_ATTR, ipi.getXmlName());
    450                writeTag(writer, ClassModelTags.LOOKUP_PROPERTY_TAG, props, ipi.getComments());
    451            }
    452        }
    453
    454        /**
    455         * Returns the writer support object.
    456         *
    457         * @return The writer support object.
    458         */
    459        public XMLWriterSupport getWriterSupport() {
    460            return this.writerSupport;
    461        }
    462    }