    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
    043package org.jfree.xml.generator;
    044
    045import java.io.IOException;
    046import java.io.Writer;
    047
    048import org.jfree.xml.generator.model.ClassDescription;
    049import org.jfree.xml.generator.model.Comments;
    050import org.jfree.xml.generator.model.DescriptionModel;
    051import org.jfree.xml.generator.model.IgnoredPropertyInfo;
    052import org.jfree.xml.generator.model.ManualMappingInfo;
    053import org.jfree.xml.generator.model.MultiplexMappingInfo;
    054import org.jfree.xml.generator.model.PropertyInfo;
    055import org.jfree.xml.generator.model.PropertyType;
    056import org.jfree.xml.generator.model.TypeInfo;
    057import org.jfree.xml.util.ClassModelTags;
    058import org.jfree.xml.writer.AttributeList;
    059import org.jfree.xml.writer.SafeTagList;
    060import org.jfree.xml.writer.XMLWriterSupport;
    061
    062/**
    063 * A model writer.
    064 */
    065public 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}