content: /* ========================================================================
   * JCommon : a free general purpose class library for the Java(tm) platform
   * ========================================================================
   *
   * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   * 
   * Project Info:  http://www.jfree.org/jcommon/index.html
   *
   * This library is free software; you can redistribute it and/or modify it 
   * under the terms of the GNU Lesser General Public License as published by 
   * the Free Software Foundation; either version 2.1 of the License, or 
   * (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful, but 
   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
   * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
   * License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
   * USA.  
   *
   * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
   * in the United States and other countries.]
   * 
   * ----------------
   * ModelWriter.java
   * ----------------
   * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
   *
   * Original Author:  Thomas Morgner;
   * Contributor(s):   David Gilbert (for Object Refinery Limited);
   *
   * $Id: ModelWriter.java,v 1.3 2005/10/18 13:32:20 mungady Exp $
   *
   * Changes
   * -------------------------
   * 21.06.2003 : Initial version
   *
   */
  
  package org.jfree.xml.generator;
  
  import java.io.IOException;
  import java.io.Writer;
  
  import org.jfree.xml.generator.model.ClassDescription;
  import org.jfree.xml.generator.model.Comments;
  import org.jfree.xml.generator.model.DescriptionModel;
  import org.jfree.xml.generator.model.IgnoredPropertyInfo;
  import org.jfree.xml.generator.model.ManualMappingInfo;
  import org.jfree.xml.generator.model.MultiplexMappingInfo;
  import org.jfree.xml.generator.model.PropertyInfo;
  import org.jfree.xml.generator.model.PropertyType;
  import org.jfree.xml.generator.model.TypeInfo;
  import org.jfree.xml.util.ClassModelTags;
  import org.jfree.xml.writer.AttributeList;
  import org.jfree.xml.writer.SafeTagList;
  import org.jfree.xml.writer.XMLWriterSupport;
  
  /**
   * A model writer.
   */
  public class ModelWriter {
  
      /** The tags that can be split. */
      private static SafeTagList safeTags;
  
      /**
       * Returns the safe tag list.
       * 
       * @return The safe tag list.
       */
      public static SafeTagList getSafeTags() {
          if (safeTags == null) {
              safeTags = new SafeTagList();
              safeTags.add(ClassModelTags.OBJECTS_TAG);
              safeTags.add(ClassModelTags.OBJECT_TAG);
              safeTags.add(ClassModelTags.CONSTRUCTOR_TAG);
              safeTags.add(ClassModelTags.ELEMENT_PROPERTY_TAG);
              safeTags.add(ClassModelTags.LOOKUP_PROPERTY_TAG);
              safeTags.add(ClassModelTags.ATTRIBUTE_PROPERTY_TAG);
              safeTags.add(ClassModelTags.PARAMETER_TAG);
              safeTags.add(ClassModelTags.INCLUDE_TAG);
              safeTags.add(ClassModelTags.IGNORED_PROPERTY_TAG);
              safeTags.add(ClassModelTags.MANUAL_TAG);
              safeTags.add(ClassModelTags.MAPPING_TAG);
              safeTags.add(ClassModelTags.TYPE_TAG);
          }
          return safeTags;
      }
  
      /** A support class for writing XML tags. */
      private XMLWriterSupport writerSupport;
      
      /** A model containing class descriptions. */
      private DescriptionModel model;
  
      /**
       * Creates a new model writer instance.
       */
      public ModelWriter() {
          this.writerSupport = new XMLWriterSupport(getSafeTags(), 0);
      }
  
      /**
       * Returns the model.
       * 
       * @return The model.
       */
      public DescriptionModel getModel() {
          return this.model;
      }
  
      /**
       * Sets the model to be written.
       * 
       * @param model  the model.
       */
      public void setModel(final DescriptionModel model) {
          this.model = model;
      }
  
      /**
       * Writes an XML header.
       * 
       * @param writer  the writer.
       * 
       * @throws IOException if there is an I/O problem.
       */
      public static void writeXMLHeader(final Writer writer) throws IOException {
          writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
          writer.write(XMLWriterSupport.getLineSeparator());
      }
  
      /**
       * Writes a set of comments.
       * 
       * @param writer  the writer.
       * @param comments  a set of comments.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeStandardComment(final Writer writer, final Comments comments) throws IOException {
          if ((comments == null) || (comments.getOpenTagComment() == null)) {
              writer.write(
                  "<!-- CVSTag: $Id: ModelWriter.java,v 1.3 2005/10/18 13:32:20 mungady Exp $ " 
                  + comments + " -->"
              );
              writer.write(XMLWriterSupport.getLineSeparator());
          }
          else {
              writeComment(writer, comments.getOpenTagComment());
          }
      }
  
      /**
       * Writes a sequence of comments.
       * 
       * @param writer  the writer.
       * @param comments  the comments (<code>null</code> ignored).
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeComment(final Writer writer, final String[] comments) throws IOException {
          if (comments == null) {
              return;
          }
          for (int i = 0; i < comments.length; i++) {
              this.writerSupport.indent(writer, XMLWriterSupport.INDENT_ONLY);
              writer.write("<!--");
              writer.write(comments[i]);
              writer.write("-->");
              writer.write(XMLWriterSupport.getLineSeparator());
          }
      }
  
      /**
       * Writes the open comments from a set of comments.
       * 
       * @param writer  the writer.
       * @param comments  the set of comments.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeOpenComment(final Writer writer, final Comments comments) throws IOException {
          if (comments == null) {
              return;
          }
          if (comments.getOpenTagComment() == null) {
              return;
          }
          writeComment(writer, comments.getOpenTagComment());
      }
  
      /**
       * Writes the close comments from a set of comments.
       * 
       * @param writer  the writer.
       * @param comments  the set of comments.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeCloseComment(final Writer writer, final Comments comments) throws IOException {
          if (comments == null) {
              return;
          }
          if (comments.getCloseTagComment() == null) {
              return;
          }
          writeComment(writer, comments.getCloseTagComment());
      }
  
      /**
       * Writes a closed (short) tag with eventually nested comments.
       *
       * @param writer  the writer.
       * @param tagName  the tag name.
       * @param attributes  the attributes.
       * @param comments  the comments.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeTag(final Writer writer, 
                              final String tagName,
                              final AttributeList attributes,
                              final Comments comments) throws IOException {
          if (comments == null) {
              this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.CLOSE);
          }
          else {
              writeOpenComment(writer, comments);
              if (comments.getCloseTagComment() != null) {
                  this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.OPEN);
                  writeCloseComment(writer, comments);
                  this.writerSupport.writeCloseTag(writer, tagName);
              }
              else {
                  this.writerSupport.writeTag(writer, tagName, attributes, XMLWriterSupport.CLOSE);
              }
          }
      }
  
      /**
       * Writes a closed (short) tag with eventually nested comments.
       *
       * @param writer  the writer.
       * @param tagName  the tag name.
       * @param attribute  the attribute name.
       * @param value  the attribute value.
       * @param comments  the comments.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeTag(final Writer writer, 
                              final String tagName,
                              final String attribute, 
                              final String value,
                              final Comments comments) throws IOException {
          if (comments == null) {
              this.writerSupport.writeTag(writer, tagName, attribute, value , XMLWriterSupport.CLOSE);
          }
          else {
              writeOpenComment(writer, comments);
              if (comments.getCloseTagComment() != null) {
                  this.writerSupport.writeTag(
                      writer, tagName, attribute, value, XMLWriterSupport.OPEN
                  );
                  writeCloseComment(writer, comments);
                  this.writerSupport.writeCloseTag(writer, tagName);
              }
              else {
                  this.writerSupport.writeTag(
                      writer, tagName, attribute, value, XMLWriterSupport.CLOSE
                  );
              }
          }
      }
  
      /**
       * Writes a model to the specified writer.
       * 
       * @param writer  the writer.
       * 
       * @throws IOException if there is an I/O problem.
       */
      public void write(final Writer writer) throws IOException {
          
          writeStandardComment(writer, this.model.getModelComments());
          this.writerSupport.writeTag(writer, ClassModelTags.OBJECTS_TAG);
          final String[] sources = this.model.getSources();
          for (int i = 0; i < sources.length; i++) {
              final Comments comments = this.model.getIncludeComment(sources[i]);
              writeTag(
                  writer, 
                  ClassModelTags.INCLUDE_TAG, ClassModelTags.SOURCE_ATTR, sources[i], comments
              );
          }
  
          for (int i = 0; i < this.model.size(); i++) {
              final ClassDescription cd = this.model.get(i);
              writeClassDescription(writer, cd);
          }
  
          final ManualMappingInfo[] mappings = getModel().getMappingModel().getManualMapping();
          for (int i = 0; i < mappings.length; i++) {
              final ManualMappingInfo mi = mappings[i];
              writeManualMapping(writer, mi);
          }
  
          final MultiplexMappingInfo[] mmappings = getModel().getMappingModel().getMultiplexMapping();
          for (int i = 0; i < mmappings.length; i++) {
              final MultiplexMappingInfo mi = mmappings[i];
              writeMultiplexMapping(writer, mi);
          }
  
          writeCloseComment(writer, this.model.getModelComments());
          this.writerSupport.writeCloseTag(writer, ClassModelTags.OBJECTS_TAG);
          
      }
  
      /**
       * Writes a manual mapping to the XML output.
       * 
       * @param writer  the writer.
       * @param mi  the mapping info.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeManualMapping(final Writer writer, final ManualMappingInfo mi) throws IOException {
          final AttributeList al = new AttributeList();
          al.setAttribute(ClassModelTags.CLASS_ATTR, mi.getBaseClass().getName());
          al.setAttribute(ClassModelTags.READ_HANDLER_ATTR, mi.getReadHandler().getName());
          al.setAttribute(ClassModelTags.WRITE_HANDLER_ATTR, mi.getWriteHandler().getName());
          writeTag(writer, ClassModelTags.MANUAL_TAG, al, mi.getComments());
      }
  
      /**
       * Writes a multiplex mapping to the XML output.
       * 
       * @param writer  the writer.
       * @param mi  the mapping info.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeMultiplexMapping(final Writer writer, final MultiplexMappingInfo mi)
          throws IOException {
          
          final TypeInfo[] tis = mi.getChildClasses();
  
          final AttributeList al = new AttributeList();
          al.setAttribute(ClassModelTags.BASE_CLASS_ATTR, mi.getBaseClass().getName());
          al.setAttribute(ClassModelTags.TYPE_ATTR, mi.getTypeAttribute());
          getWriterSupport().writeTag(writer, ClassModelTags.MAPPING_TAG, al, XMLWriterSupport.OPEN);
  
          for (int j = 0; j < tis.length; j++) {
              final AttributeList tiAttr = new AttributeList();
              tiAttr.setAttribute(ClassModelTags.NAME_ATTR, tis[j].getName());
              tiAttr.setAttribute(ClassModelTags.CLASS_ATTR, tis[j].getType().getName());
              writeTag(writer, ClassModelTags.TYPE_TAG, tiAttr, tis[j].getComments());
          }
  
          getWriterSupport().writeCloseTag(writer, ClassModelTags.MAPPING_TAG);
      }
  
      /**
       * Writes a class description.
       * 
       * @param writer  the writer.
       * @param cd  the class description.
       * 
       * @throws IOException if there is an I/O problem.
       */
      protected void writeClassDescription(final Writer writer, final ClassDescription cd) throws IOException {
  
          if (cd.isUndefined()) {
              return;
          }
  
          final AttributeList al = new AttributeList();
          al.setAttribute(ClassModelTags.CLASS_ATTR, cd.getName());
          if (cd.getRegisterKey() != null) {
              al.setAttribute(ClassModelTags.REGISTER_NAMES_ATTR, cd.getRegisterKey());
          }
          if (cd.isPreserve()) {
              al.setAttribute(ClassModelTags.IGNORE_ATTR, "true");
          }
          this.writerSupport.writeTag(writer, ClassModelTags.OBJECT_TAG, al, XMLWriterSupport.OPEN);
  
          final TypeInfo[] constructorInfo = cd.getConstructorDescription();
          if (constructorInfo != null && constructorInfo.length != 0) {
              this.writerSupport.writeTag(writer, ClassModelTags.CONSTRUCTOR_TAG);
              for (int i = 0; i < constructorInfo.length; i++) {
                  final AttributeList constructorList = new AttributeList();
                  constructorList.setAttribute(
                      ClassModelTags.CLASS_ATTR, constructorInfo[i].getType().getName()
                  );
                  constructorList.setAttribute(
                      ClassModelTags.PROPERTY_ATTR, constructorInfo[i].getName()
                  );
                  writeTag(writer, ClassModelTags.PARAMETER_TAG, constructorList, 
                           constructorInfo[i].getComments());
              }
              this.writerSupport.writeCloseTag(writer, ClassModelTags.CONSTRUCTOR_TAG);
          }
  
          final PropertyInfo[] properties = cd.getProperties();
          for (int i = 0; i < properties.length; i++) {
              writePropertyInfo(writer, properties[i]);
          }
  
          this.writerSupport.writeCloseTag(writer, ClassModelTags.OBJECT_TAG);
      }
  
      /**
       * Writes a property info element.
       * 
       * @param writer  the writer.
       * @param ipi  the property info.
       * 
       * @throws IOException if there is an I/O problem.
       */
      private void writePropertyInfo(final Writer writer, final PropertyInfo ipi) throws IOException {
          final AttributeList props = new AttributeList();
          props.setAttribute(ClassModelTags.NAME_ATTR, ipi.getName());
  
          if (ipi instanceof IgnoredPropertyInfo) {
              writeTag(writer, ClassModelTags.IGNORED_PROPERTY_TAG, props, ipi.getComments());
              return;
          }
  
          if (ipi.getPropertyType().equals(PropertyType.ATTRIBUTE)) {
              props.setAttribute(ClassModelTags.ATTRIBUTE_ATTR, ipi.getXmlName());
              props.setAttribute(ClassModelTags.ATTRIBUTE_HANDLER_ATTR, ipi.getXmlHandler());
              writeTag(writer, ClassModelTags.ATTRIBUTE_PROPERTY_TAG, props, ipi.getComments());
          }
          else if (ipi.getPropertyType().equals(PropertyType.ELEMENT)) {
              if (ipi.getComments() == null || ipi.getComments().getOpenTagComment() == null)
              {
                  this.writerSupport.indent(writer, XMLWriterSupport.INDENT_ONLY);
                  writer.write("<!-- property type is " + ipi.getType() + " -->");
                  writer.write(System.getProperty("line.separator", "\n"));
              }
              props.setAttribute(ClassModelTags.ELEMENT_ATTR, ipi.getXmlName());
              writeTag(writer, ClassModelTags.ELEMENT_PROPERTY_TAG, props, ipi.getComments());
          }
          else {
              props.setAttribute(ClassModelTags.LOOKUP_ATTR, ipi.getXmlName());
              writeTag(writer, ClassModelTags.LOOKUP_PROPERTY_TAG, props, ipi.getComments());
          }
      }
  
      /**
       * Returns the writer support object.
       * 
       * @return The writer support object.
       */
      public XMLWriterSupport getWriterSupport() {
          return this.writerSupport;
      }
  }
  