You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ddlutils-dev@db.apache.org by to...@apache.org on 2011/05/02 07:41:14 UTC

svn commit: r1098483 [1/2] - in /db/ddlutils/trunk/src: main/java/org/apache/ddlutils/dynabean/ main/java/org/apache/ddlutils/io/ main/java/org/apache/ddlutils/util/ test/java/org/apache/ddlutils/io/ test/java/org/apache/ddlutils/util/

Author: tomdz
Date: Mon May  2 05:41:14 2011
New Revision: 1098483

URL: http://svn.apache.org/viewvc?rev=1098483&view=rev
Log:
Fix for DDLUTILS-245: writeDataToFile produce broken XML

Added:
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ColumnXmlWriter.java
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ModelXmlWriter.java
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/TableXmlWriter.java
    db/ddlutils/trunk/src/test/java/org/apache/ddlutils/util/
    db/ddlutils/trunk/src/test/java/org/apache/ddlutils/util/DatabaseTestHelper.java
      - copied unchanged from r1002941, db/ddlutils/trunk/src/main/java/org/apache/ddlutils/util/DatabaseTestHelper.java
Removed:
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/util/DatabaseTestHelper.java
Modified:
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/dynabean/SqlDynaBean.java
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataReader.java
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataWriter.java
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java
    db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/XMLUtils.java
    db/ddlutils/trunk/src/test/java/org/apache/ddlutils/io/TestDataReaderAndWriter.java

Modified: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/dynabean/SqlDynaBean.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/dynabean/SqlDynaBean.java?rev=1098483&r1=1098482&r2=1098483&view=diff
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/dynabean/SqlDynaBean.java (original)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/dynabean/SqlDynaBean.java Mon May  2 05:41:14 2011
@@ -67,4 +67,49 @@ public class SqlDynaBean extends BasicDy
         }
         return result.toString();
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode()
+    {
+        return toString().hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object obj)
+    {
+        if (obj instanceof SqlDynaBean)
+        {
+            SqlDynaBean other     = (SqlDynaBean)obj;
+            DynaClass   dynaClass = getDynaClass();
+
+            if (dynaClass.equals(other.getDynaClass()))
+            {
+                DynaProperty[] props = dynaClass.getDynaProperties();
+
+                for (int idx = 0; idx < props.length; idx++)
+                {
+                    Object value      = get(props[idx].getName());
+                    Object otherValue = other.get(props[idx].getName());
+
+                    if (value == null)
+                    {
+                        if (otherValue != null)
+                        {
+                            return false;
+                        }
+                    }
+                    else
+                    {
+                        return value.equals(otherValue);
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
 }

Added: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ColumnXmlWriter.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ColumnXmlWriter.java?rev=1098483&view=auto
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ColumnXmlWriter.java (added)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ColumnXmlWriter.java Mon May  2 05:41:14 2011
@@ -0,0 +1,176 @@
+package org.apache.ddlutils.io;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.ddlutils.model.Column;
+
+/**
+ * Helper class for writing columns to XML.  
+ */
+public class ColumnXmlWriter extends ModelXmlWriter
+{
+    private final int AS_TABLE_ATTRIBUTE  = 0;
+    private final int AS_SUBTAG           = 1;
+    private final int AS_COLUMN_ATTRIBUTE = 2;
+    private final int AS_VALUE            = 3;
+
+    private final String columnName;
+    private final String columnValue;
+    private final boolean nameBase64Encoded;
+    private final boolean valueBase64Encoded;
+    private final int columnFormattingMethod;
+
+    /**
+     * Creates a new column writer.
+     * 
+     * @param column The column, cannot be null
+     * @param value  The value, cannot be null
+     */
+    public ColumnXmlWriter(Column column, String value)
+    {
+        /*
+         * - attribute "column name"="column value" in the parent's (table) element
+         *   iff the column name is a valid attribute name and is not "table-name" and not "column",
+         *   and the value is a valid attribute value not longer than 255 characters
+         * - otherwise, writes a sub-element <column> with an attribute column-name that contains the name
+         *   of the column, and the body of that sub-element contains the column value,
+         *   iff the column name is a valid attribute value not longer than 255 characters. If the column
+         *   value contains illegal characters, then the column sub element will have a "base64" attribute
+         *   with the value "true" and the value will be base64 encoded
+         * - otherwise writes a sub-element <column> with a sub-element <column-name> whose
+         *   body is the name of the column, and another sub-element <column-value> whose body contains
+         *   the column value. If either the column name or value contain illegal characters, then the
+         *   corresponding sub element will have a "base64" attribute with the value "true" and its body will
+         *   be base64 encoded.
+         */
+        if (XMLUtils.hasIllegalXMLCharacters(value))
+        {
+            columnValue        = XMLUtils.base64Encode(value);
+            valueBase64Encoded = true;
+        }
+        else
+        {
+            columnValue        = value;
+            valueBase64Encoded = false;
+        }
+
+        if (XMLUtils.hasIllegalXMLCharacters(column.getName())) {
+            columnName             = XMLUtils.base64Encode(column.getName());
+            nameBase64Encoded      = true;
+            columnFormattingMethod = AS_VALUE;
+        }
+        else
+        {
+            columnName        = column.getName();
+            nameBase64Encoded = false;
+            if (columnName.length() > XMLUtils.MAX_NAME_LENGTH)
+            {
+                columnFormattingMethod = AS_VALUE;
+            }
+            else if ("table-name".equals(columnName) ||
+                     "column".equals(columnName) ||
+                     DatabaseIO.BASE64_ATTR_NAME.equals(columnName) ||
+                     !XMLUtils.isWellFormedXMLName(columnName))
+            {
+                columnFormattingMethod = AS_COLUMN_ATTRIBUTE;
+            }
+            else if (valueBase64Encoded || (value.length() > XMLUtils.MAX_ATTRIBUTE_LENGTH))
+            {
+                columnFormattingMethod = AS_SUBTAG;
+            }
+            else
+            {
+                columnFormattingMethod = AS_TABLE_ATTRIBUTE;
+            }
+        }
+    }
+
+    /**
+     * Writes the column data as an attribute of the parent element if possible.
+     * Does nothing if the column name or value cannot be used in an attribute.
+     * 
+     * @param writer The writer to write to
+     * @return <code>true</code> if something was written
+     */
+    public boolean writeAttribute(DataWriter writer)
+    {
+        if (columnFormattingMethod == AS_TABLE_ATTRIBUTE)
+        {
+            writer.writeAttribute(null, columnName, columnValue);
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    /**
+     * Writes any sub elements necessary for the column. If no sub elements
+     * are required, then this method does nothing.
+     * 
+     * @param writer The writer to write to
+     * @return <code>true</code> if something was written
+     */
+    public boolean writeSubElement(DataWriter writer)
+    {
+        if (columnFormattingMethod != AS_TABLE_ATTRIBUTE)
+        {
+            writer.printlnIfPrettyPrinting();
+            writer.indentIfPrettyPrinting(2);
+            if (columnFormattingMethod == AS_SUBTAG)
+            {
+                writer.writeElementStart(null, columnName);
+                writeText(writer, columnValue, valueBase64Encoded);
+            }
+            else
+            {
+                writer.writeElementStart(null, "column");
+                if (columnFormattingMethod == AS_COLUMN_ATTRIBUTE)
+                {
+                    writer.writeAttribute(null, "column-name", columnName);
+                    writeText(writer, columnValue, valueBase64Encoded);
+                }
+                else if (columnFormattingMethod == AS_VALUE)
+                {
+                    writer.printlnIfPrettyPrinting();
+                    writer.indentIfPrettyPrinting(3);
+                    writer.writeElementStart(null, "column-name");
+                    writeText(writer, columnName, nameBase64Encoded);
+                    writer.writeElementEnd();
+
+                    writer.printlnIfPrettyPrinting();
+                    writer.indentIfPrettyPrinting(3);
+                    writer.writeElementStart(null, "column-value");
+                    writeText(writer, columnValue, valueBase64Encoded);
+                    writer.writeElementEnd();
+                    writer.printlnIfPrettyPrinting();
+                    writer.indentIfPrettyPrinting(2);
+                }
+            }
+            writer.writeElementEnd();
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+}

Modified: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataReader.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataReader.java?rev=1098483&r1=1098482&r2=1098483&view=diff
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataReader.java (original)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataReader.java Mon May  2 05:41:14 2011
@@ -27,8 +27,11 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
 import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamReader;
@@ -311,48 +314,62 @@ public class DataReader
      */
     private void readBean(XMLStreamReader xmlReader) throws XMLStreamException, DdlUtilsXMLException
     {
-        QName elemQName = xmlReader.getName();
-        Table table     = _model.findTable(elemQName.getLocalPart(), isCaseSensitive());
+        QName    elemQName  = xmlReader.getName();
+        Location location   = xmlReader.getLocation();
+        Map      attributes = new HashMap();
+        String   tableName  = null;
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            attributes.put(isCaseSensitive() ? attrQName.getLocalPart() : attrQName.getLocalPart().toLowerCase(),
+                           xmlReader.getAttributeValue(idx));
+        }
+        readColumnSubElements(xmlReader, attributes);
+
+        if ("table".equals(elemQName.getLocalPart()))
+        {
+            tableName = (String)attributes.get("table-name");
+        }
+        else
+        {
+            tableName  = elemQName.getLocalPart();
+        }
+
+        Table table = _model.findTable(tableName, isCaseSensitive());
 
         if (table == null)
         {
-            _log.warn("Data XML contains an element " + elemQName + " at location " + xmlReader.getLocation() +
+            _log.warn("Data XML contains an element " + elemQName + " at location " + location +
                       " but there is no table defined with this name. This element will be ignored.");
-            readOverElement(xmlReader);
         }
         else
         {
             DynaBean bean = _model.createDynaBeanFor(table);
-    
-            for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+
+            for (int idx = 0; idx < table.getColumnCount(); idx++)
             {
-                QName  attrQName = xmlReader.getAttributeName(idx);
-                Column column    = table.findColumn(attrQName.getLocalPart(), isCaseSensitive());
+                Column column = table.getColumn(idx);
+                String value  = (String)attributes.get(isCaseSensitive() ? column.getName() : column.getName().toLowerCase());
 
-                if (column == null)
+                if (value != null)
                 {
-                    _log.warn("Data XML contains an attribute " + attrQName + " at location " + xmlReader.getLocation() +
-                              " but there is no column defined in table " + table.getName() + " with this name. This attribute will be ignored.");
-                }
-                else
-                {
-                    setColumnValue(bean, table, column, xmlReader.getAttributeValue(idx));
+                    setColumnValue(bean, table, column, value);
                 }
             }
-            readColumnSubElements(xmlReader, bean, table);
             getSink().addBean(bean);
             consumeRestOfElement(xmlReader);
         }
     }
 
     /**
-     * Reads all column sub elements that match the columns specified by the given table object from the xml reader into the given bean.
+     * Reads all relevant sub elements that match the columns specified by the given table object from the xml reader into the given bean.
      *  
      * @param xmlReader The reader
-     * @param bean      The bean to fill
-     * @param table     The table definition
+     * @param data      Where to store the values
      */
-    private void readColumnSubElements(XMLStreamReader xmlReader, DynaBean bean, Table table) throws XMLStreamException, DdlUtilsXMLException
+    private void readColumnSubElements(XMLStreamReader xmlReader, Map data) throws XMLStreamException, DdlUtilsXMLException
     {
         int eventType = XMLStreamReader.START_ELEMENT;
 
@@ -361,7 +378,7 @@ public class DataReader
             eventType = xmlReader.next();
             if (eventType == XMLStreamReader.START_ELEMENT)
             {
-                readColumnSubElement(xmlReader, bean, table);
+                readColumnSubElement(xmlReader, data);
             }
         }
     }
@@ -370,48 +387,128 @@ public class DataReader
      * Reads the next column sub element that matches a column specified by the given table object from the xml reader into the given bean.
      *  
      * @param xmlReader The reader
-     * @param bean      The bean to fill
-     * @param table     The table definition
+     * @param data      Where to store the values
      */
-    private void readColumnSubElement(XMLStreamReader xmlReader, DynaBean bean, Table table) throws XMLStreamException, DdlUtilsXMLException
+    private void readColumnSubElement(XMLStreamReader xmlReader, Map data) throws XMLStreamException, DdlUtilsXMLException
     {
         QName   elemQName  = xmlReader.getName();
+        Map     attributes = new HashMap();
         boolean usesBase64 = false;
 
         for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
         {
-            QName attrQName = xmlReader.getAttributeName(idx);
+            QName  attrQName = xmlReader.getAttributeName(idx);
+            String value     = xmlReader.getAttributeValue(idx);
 
-            if (DatabaseIO.BASE64_ATTR_NAME.equals(attrQName.getLocalPart()) &&
-                "true".equalsIgnoreCase(xmlReader.getAttributeValue(idx)))
+            if (DatabaseIO.BASE64_ATTR_NAME.equals(attrQName.getLocalPart()))
             {
-                usesBase64 = true;
-                break;
+                if ("true".equalsIgnoreCase(value))
+                {
+                    usesBase64 = true;
+                }
+            }
+            else
+            {
+                attributes.put(attrQName.getLocalPart(), value);
             }
         }
 
-        Column column  = table.findColumn(elemQName.getLocalPart(), isCaseSensitive());
+        int          eventType = XMLStreamReader.START_ELEMENT;
+        StringBuffer content   = new StringBuffer();
 
-        if (column == null)
+        while (eventType != XMLStreamReader.END_ELEMENT)
         {
-            _log.warn("Data XML contains an element " + elemQName + " at location " + xmlReader.getLocation() +
-                      " but there is no column defined in table " + table.getName() + " with this name. This element will be ignored.");
+            eventType = xmlReader.next();
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                readColumnDataSubElement(xmlReader, attributes);
+            }
+            else if ((eventType == XMLStreamReader.CHARACTERS) ||
+                     (eventType == XMLStreamReader.CDATA) ||
+                     (eventType == XMLStreamReader.SPACE) ||
+                     (eventType == XMLStreamReader.ENTITY_REFERENCE))
+            {
+                content.append(xmlReader.getText());
+            }
         }
-        else
+
+        String value = content.toString().trim();
+
+        if (usesBase64)
         {
-            String value = xmlReader.getElementText();
+            value = new String(Base64.decodeBase64(value.getBytes()));
+        }
 
-            if (value != null)
+        String name = elemQName.getLocalPart();
+
+        if ("table-name".equals(name))
+        {
+            data.put("table-name", value);
+        }
+        else
+        {
+            if ("column".equals(name))
             {
-                value = value.trim();
+                name = (String)attributes.get("column-name");
+            }
+            if (attributes.containsKey("column-value"))
+            {
+                value = (String)attributes.get("column-value");
+            }
+            data.put(name, value);
+        }
+        consumeRestOfElement(xmlReader);
+    }
 
-                if (usesBase64)
+
+    /**
+     * Reads the next column-name or column-value sub element.
+     *  
+     * @param xmlReader The reader
+     * @param data      Where to store the values
+     */
+    private void readColumnDataSubElement(XMLStreamReader xmlReader, Map data) throws XMLStreamException, DdlUtilsXMLException
+    {
+        QName   elemQName  = xmlReader.getName();
+        boolean usesBase64 = false;
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName  attrQName = xmlReader.getAttributeName(idx);
+            String value     = xmlReader.getAttributeValue(idx);
+
+            if (DatabaseIO.BASE64_ATTR_NAME.equals(attrQName.getLocalPart()))
+            {
+                if ("true".equalsIgnoreCase(value))
                 {
-                    value = new String(Base64.decodeBase64(value.getBytes()));
+                    usesBase64 = true;
                 }
-                setColumnValue(bean, table, column, value);
+                break;
             }
         }
+
+        String value = xmlReader.getElementText();
+
+        if (value != null)
+        {
+            value = value.toString().trim();
+    
+            if (usesBase64)
+            {
+                value = new String(Base64.decodeBase64(value.getBytes()));
+            }
+        }
+
+        String name = elemQName.getLocalPart();
+
+        if ("column-name".equals(name))
+        {
+            data.put("column-name", value);
+        }
+        else if ("column-value".equals(name))
+        {
+            data.put("column-value", value);
+        }
         consumeRestOfElement(xmlReader);
     }
 
@@ -445,33 +542,6 @@ public class DataReader
             throw new DdlUtilsXMLException("Could not set bean property for column " + column.getName(), ex);
         }
     }
-
-    // TODO: move these two into a helper class:
-    
-    /**
-     * Reads over the current element. This assumes that the current XML stream event type is
-     * START_ELEMENT.
-     *  
-     * @param reader The xml reader
-     */
-    private void readOverElement(XMLStreamReader reader) throws XMLStreamException
-    {
-        int depth = 1;
-
-        while (depth > 0)
-        {
-            int eventType = reader.next();
-
-            if (eventType == XMLStreamReader.START_ELEMENT)
-            {
-                depth++;
-            }
-            else if (eventType == XMLStreamReader.END_ELEMENT)
-            {
-                depth--;
-            }
-        }
-    }
     
     /**
      * Consumes the rest of the current element. This assumes that the current XML stream

Modified: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataWriter.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataWriter.java?rev=1098483&r1=1098482&r2=1098483&view=diff
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataWriter.java (original)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/DataWriter.java Mon May  2 05:41:14 2011
@@ -20,39 +20,25 @@ package org.apache.ddlutils.io;
  */
 
 import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-
 import org.apache.commons.beanutils.DynaBean;
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.ddlutils.dynabean.SqlDynaBean;
 import org.apache.ddlutils.dynabean.SqlDynaClass;
-import org.apache.ddlutils.io.converters.ConversionException;
 import org.apache.ddlutils.io.converters.SqlTypeConverter;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Table;
 
 /**
  * Writes dyna beans matching a specified database model into an XML file.
- * 
- * TODO: Make names (tables, columns) XML-compliant
- * 
- * @version $Revision: 289996 $
  */
 public class DataWriter extends PrettyPrintingXmlWriter
 {
-    /** String values with a size not bigger than this value will be written to attributes;
-        if their size is longer, then a sub element is generated instead. */ 
-    private static final int MAX_ATTRIBUTE_LENGTH = 255;
-
     /** Our log. */
     private final Log _log = LogFactory.getLog(DataWriter.class);
 
@@ -139,162 +125,36 @@ public class DataWriter extends PrettyPr
      */
     public void write(SqlDynaBean bean) throws DataWriterException
     {
-        SqlDynaClass dynaClass   = (SqlDynaClass)bean.getDynaClass();
-        Table        table       = dynaClass.getTable();
-        HashMap      subElements = new HashMap();
+        SqlDynaClass   dynaClass     = (SqlDynaClass)bean.getDynaClass();
+        Table          table         = dynaClass.getTable();
+        TableXmlWriter tableWriter   = new TableXmlWriter(table);
+        List           columnWriters = new ArrayList();
 
-        try
+        for (int idx = 0; idx < table.getColumnCount(); idx++)
         {
-            indentIfPrettyPrinting(1);
-            writeElementStart(null, table.getName());
-            for (int idx = 0; idx < table.getColumnCount(); idx++)
-            {
-                Column           column      = table.getColumn(idx);
-                Object           value       = bean.get(column.getName());
-                SqlTypeConverter converter   = _converterConf.getRegisteredConverter(table, column);
-                String           valueAsText = null;
+            Column           column      = table.getColumn(idx);
+            Object           value       = bean.get(column.getName());
+            SqlTypeConverter converter   = _converterConf.getRegisteredConverter(table, column);
+            String           valueAsText = null;
 
-                if (converter == null)
-                {
-                    if (value != null)
-                    {
-                        valueAsText = value.toString();
-                    }
-                }
-                else
-                {
-                    valueAsText = converter.convertToString(value, column.getTypeCode());
-                }
-                if (valueAsText != null)
-                {
-                    // we create an attribute only if the text is not too long
-                    // and if it does not contain special characters
-                    if ((valueAsText.length() > MAX_ATTRIBUTE_LENGTH) || analyzeText(valueAsText, null))
-                    {
-                        // we defer writing the sub elements
-                        subElements.put(column.getName(), valueAsText);
-                    }
-                    else
-                    {
-                        writeAttribute(null, column.getName(), valueAsText);
-                    }
-                }
-            }
-            if (!subElements.isEmpty())
+            if (converter == null)
             {
-                List cutPoints = new ArrayList();
-
-                for (Iterator it = subElements.entrySet().iterator(); it.hasNext();)
+                if (value != null)
                 {
-                    Map.Entry entry     = (Map.Entry)it.next();
-                    String    content   = entry.getValue().toString();
-
-                    printlnIfPrettyPrinting();
-                    indentIfPrettyPrinting(2);
-                    writeElementStart(null, entry.getKey().toString());
-
-                    // if the content contains special characters, we have to apply base64 encoding to it
-                    // if the content is too short, then it has to contain special characters (otherwise
-                    // it would have been written as an attribute already), otherwise we check
-                    cutPoints.clear();
-
-                    boolean writeBase64Encoded = analyzeText(content, cutPoints);
-
-                    if (writeBase64Encoded)
-                    {
-                        writeAttribute(null, DatabaseIO.BASE64_ATTR_NAME, "true");
-                        try {
-                            writeCData(new String(Base64.encodeBase64(content.getBytes()), getEncoding()));
-                        }
-                        catch (UnsupportedEncodingException ex) {
-                            throw new DataWriterException(ex);
-                        }
-                    }
-                    else
-                    {
-                        if (cutPoints.isEmpty())
-                        {
-                            writeCData(content);
-                        }
-                        else
-                        {
-                            int lastPos = 0;
-
-                            for (Iterator cutPointIt = cutPoints.iterator(); cutPointIt.hasNext();)
-                            {
-                                int curPos = ((Integer)cutPointIt.next()).intValue();
-
-                                writeCData(content.substring(lastPos, curPos));
-                                lastPos = curPos;
-                            }
-                            if (lastPos < content.length())
-                            {
-                                writeCData(content.substring(lastPos));
-                            }
-                        }
-                    }
-
-                    writeElementEnd();
+                    valueAsText = value.toString();
                 }
-                printlnIfPrettyPrinting();
-                indentIfPrettyPrinting(1);
             }
-            writeElementEnd();
-            printlnIfPrettyPrinting();
-        }
-        catch (ConversionException ex)
-        {
-            throw new DataWriterException(ex);
-        }
-    }
-
-    /**
-     * Determines whether the given string contains special characters that cannot
-     * be used in XML, and if not, finds the cut points where to split the text
-     * when writing it in a CDATA section.
-     * 
-     * @param text      The text
-     * @param cutPoints Will be filled with cut points to split the text when writing it
-     *                  in a CDATA section (only if the method returns <code>false</code>)
-     * @return <code>true</code> if the text contains special characters
-     */
-    private boolean analyzeText(String text, List cutPoints)
-    {
-        List tmpCutPoints          = cutPoints == null ? null : new ArrayList();
-        int  numChars              = text.length();
-        int  numFoundCDataEndChars = 0;
-
-        for (int charPos = 0; charPos < numChars; charPos++)
-        {
-            char c = text.charAt(charPos);
-
-            if ((c < 0x0020) && (c != '\n') && (c != '\r') && (c != '\t'))
+            else
             {
-                return true;
+                valueAsText = converter.convertToString(value, column.getTypeCode());
             }
-            else if (cutPoints != null)
+            if (valueAsText != null)
             {
-                if ((c == ']') && ((numFoundCDataEndChars == 0) || (numFoundCDataEndChars == 1)))
-                {
-                    numFoundCDataEndChars++;
-                }
-                else if ((c == '>') && (numFoundCDataEndChars == 2))
-                {
-                    // we have to split the CDATA right here before the '>' (see DDLUTILS-174)
-                    tmpCutPoints.add(new Integer(charPos));
-                    numFoundCDataEndChars = 0;
-                }
-                else
-                {
-                    numFoundCDataEndChars = 0;
-                }
+                columnWriters.add(new ColumnXmlWriter(column, valueAsText));
             }
         }
-        if (cutPoints != null)
-        {
-            cutPoints.addAll(tmpCutPoints);
-        }
-        return false;
+
+        tableWriter.write(columnWriters, this);
     }
 
     /**

Added: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ModelXmlWriter.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ModelXmlWriter.java?rev=1098483&view=auto
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ModelXmlWriter.java (added)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/ModelXmlWriter.java Mon May  2 05:41:14 2011
@@ -0,0 +1,64 @@
+package org.apache.ddlutils.io;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Base class providing helper functions for writing model elements to XML.  
+ */
+public abstract class ModelXmlWriter
+{
+    protected void writeText(DataWriter writer, String value, boolean isBase64Encoded)
+    {
+        if (isBase64Encoded)
+        {
+            writer.writeAttribute(null, "base64", "true");
+            writer.writeCharacters(value);
+        }
+        else
+        {
+            List cutPoints = XMLUtils.findCDataCutPoints(value);
+    
+            // if the content contains special characters, we have to apply base64 encoding to it
+            if (cutPoints.isEmpty())
+            {
+                writer.writeCharacters(value);
+            }
+            else
+            {
+                int lastPos = 0;
+    
+                for (Iterator cutPointIt = cutPoints.iterator(); cutPointIt.hasNext();)
+                {
+                    int curPos = ((Integer)cutPointIt.next()).intValue();
+    
+                    writer.writeCData(value.substring(lastPos, curPos));
+                    lastPos = curPos;
+                }
+                if (lastPos < value.length())
+                {
+                    writer.writeCData(value.substring(lastPos));
+                }
+            }
+        }
+    }
+}

Modified: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java?rev=1098483&r1=1098482&r2=1098483&view=diff
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java (original)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java Mon May  2 05:41:14 2011
@@ -373,4 +373,24 @@ public class PrettyPrintingXmlWriter
             }
         }
     }
+
+    /**
+     * Writes a text segment.
+     * 
+     * @param data The data to write
+     */
+    public void writeCharacters(String data) throws DdlUtilsXMLException
+    {
+        if (data != null)
+        {
+            try
+            {
+                _writer.writeCharacters(data);
+            }
+            catch (XMLStreamException ex)
+            {
+                throwException(ex);
+            }
+        }
+    }
 }

Added: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/TableXmlWriter.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/TableXmlWriter.java?rev=1098483&view=auto
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/TableXmlWriter.java (added)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/TableXmlWriter.java Mon May  2 05:41:14 2011
@@ -0,0 +1,115 @@
+package org.apache.ddlutils.io;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Iterator;
+import java.util.List;
+import org.apache.ddlutils.model.Table;
+
+/**
+ * Base interface for different strategies to write the XML for a data bean for a specific table.
+ */
+public class TableXmlWriter extends ModelXmlWriter
+{
+    private static final int AS_TAG_NAME  = 0;
+    private static final int AS_ATTRIBUTE = 1;
+    private static final int AS_SUB_TAG   = 2;
+
+    private final String tableName;
+    private final int formattingMethod;
+    private final boolean base64Encoded;
+
+    public TableXmlWriter(Table table)
+    {
+        if (XMLUtils.hasIllegalXMLCharacters(table.getName()))
+        {
+            tableName        = XMLUtils.base64Encode(table.getName());
+            formattingMethod = AS_SUB_TAG;
+            base64Encoded    = true;
+        }
+        else
+        {
+            tableName     = table.getName();
+            base64Encoded = false;
+            if (tableName.length() > XMLUtils.MAX_NAME_LENGTH)
+            {
+                formattingMethod = AS_SUB_TAG;
+            }
+            else if ("table".equals(tableName) || !XMLUtils.isWellFormedXMLName(tableName))
+            {
+                formattingMethod = AS_ATTRIBUTE;
+            }
+            else
+            {
+                formattingMethod = AS_TAG_NAME;
+            }
+        }
+    }
+
+    /**
+     * Write the table data to XML to the given writer.
+     * 
+     * @param columnXmlWriters A list of column xml writers for writing out the bean's values to XML
+     * @param writer           The writer to write to
+     */
+    public void write(List columnXmlWriters, DataWriter writer)
+    {
+        writer.indentIfPrettyPrinting(1);
+        if (formattingMethod == AS_TAG_NAME)
+        {
+            writer.writeElementStart(null, tableName);
+        }
+        else
+        {
+            writer.writeElementStart(null, "table");
+        }
+        if (formattingMethod == AS_ATTRIBUTE)
+        {
+            writer.writeAttribute(null, "table-name", tableName);
+        }
+        for (Iterator it = columnXmlWriters.iterator(); it.hasNext();)
+        {
+            ((ColumnXmlWriter)it.next()).writeAttribute(writer);
+        }
+
+        boolean hasSubTags = false;
+
+        if (formattingMethod == AS_SUB_TAG)
+        {
+            writer.printlnIfPrettyPrinting();
+            writer.indentIfPrettyPrinting(2);
+            writer.writeElementStart(null, "table-name");
+            writeText(writer, tableName, base64Encoded);
+            writer.writeElementEnd();
+            hasSubTags = true;
+        }
+        for (Iterator it = columnXmlWriters.iterator(); it.hasNext();)
+        {
+            hasSubTags = ((ColumnXmlWriter)it.next()).writeSubElement(writer) || hasSubTags;
+        }
+        if (hasSubTags)
+        {
+            writer.printlnIfPrettyPrinting();
+            writer.indentIfPrettyPrinting(1);
+        }
+        writer.writeElementEnd();
+        writer.printlnIfPrettyPrinting();
+    }
+}

Modified: db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/XMLUtils.java
URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/XMLUtils.java?rev=1098483&r1=1098482&r2=1098483&view=diff
==============================================================================
--- db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/XMLUtils.java (original)
+++ db/ddlutils/trunk/src/main/java/org/apache/ddlutils/io/XMLUtils.java Mon May  2 05:41:14 2011
@@ -19,9 +19,14 @@ package org.apache.ddlutils.io;
  * under the License.
  */
 
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.codec.binary.Base64;
+
 /**
  * <p>Contains basic utility methods for XML.</p>
- * This class is borrowed from <a href='http://commons.apache.org/betwixt/'>Apache Commons Betwixt</a>
+ * Parts of this class are borrowed from <a href='http://commons.apache.org/betwixt/'>Apache Commons Betwixt</a>
  * whose class in turn is based on code in <a href='http://xerces.apache.org/xerces2-j/index.html'>Apache Xerces</a>.
  * <p>The code for {@link #isWellFormedXMLName} is based on code in 
  * <code>org.apache.xerces.util.XMLChar</code> 
@@ -34,10 +39,14 @@ package org.apache.ddlutils.io;
  * @author Arnaud  Le Hors, IBM
  * @author Rahul Srivastava, Sun Microsystems Inc.  
  * @author Robert Burrell Donkin
- * @version $Revision: $
  */
 public class XMLUtils
 {
+    /** Maximum length of attribute values that we want to generate. */ 
+    public static final int MAX_ATTRIBUTE_LENGTH = 255;
+    /** Maximum length of a tag or attribute name that we want to generate. */ 
+    public static final int MAX_NAME_LENGTH = 255;
+
     /** Name start character mask. */
     private static final int MASK_NAME_START = 0x01;
     /** Name character mask. */
@@ -295,4 +304,88 @@ public class XMLUtils
     {
         return (c < 0x10000) && ((CHARS[c] & MASK_NAME_START) != 0);
     }
+
+    /**
+     * Determines whether the given string contains special characters that cannot
+     * be used in XML.
+     * 
+     * @param text The text
+     * @return <code>true</code> if the text contains special characters
+     */
+    public static boolean hasIllegalXMLCharacters(String text)
+    {
+        int numChars = text.length();
+
+        for (int charPos = 0; charPos < numChars; charPos++)
+        {
+            char c = text.charAt(charPos);
+
+            if ((c != 0x9) && (c != 0xA) && (c != 0xD) && ((c < 0x20) || (c > 0xD7FF)) && ((c < 0xE000) || (c > 0xFFFD)) && ((c < 0x10000) || (c > 0x10FFFF)))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Encodes the given value with Base64.
+     * 
+     * @param value The value to encode
+     * @return The encoded value
+     */
+    public static String base64Encode(String value)
+    {
+        try
+        {
+            return value == null ? null : new String(Base64.encodeBase64(value.getBytes("UTF-8")), "UTF-8");
+        }
+        catch (UnsupportedEncodingException ex)
+        {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    /**
+     * Determines whether the given string contains special characters that cannot
+     * be used in XML, and if not, finds the cut points where to split the text
+     * when writing it in a CDATA section.
+     * 
+     * @param text The text
+     * @return <code>null</code> if the text contains special characters, or the list of cut points otherwise
+     */
+    public static List findCDataCutPoints(String text)
+    {
+        List cutPoints             = new ArrayList();
+        int  numChars              = text.length();
+        int  numFoundCDataEndChars = 0;
+
+        for (int charPos = 0; charPos < numChars; charPos++)
+        {
+            char c = text.charAt(charPos);
+
+            if ((c != 0x9) && (c != 0xA) && (c != 0xD) && ((c < 0x20) || (c > 0xD7FF)) && ((c < 0xE000) || (c > 0xFFFD)) && ((c < 0x10000) || (c > 0x10FFFF)))
+            {
+                return null;
+            }
+            else
+            {
+                if ((c == ']') && ((numFoundCDataEndChars == 0) || (numFoundCDataEndChars == 1)))
+                {
+                    numFoundCDataEndChars++;
+                }
+                else if ((c == '>') && (numFoundCDataEndChars == 2))
+                {
+                    // we have to split the CDATA right here before the '>' (see DDLUTILS-174)
+                    cutPoints.add(new Integer(charPos));
+                    numFoundCDataEndChars = 0;
+                }
+                else
+                {
+                    numFoundCDataEndChars = 0;
+                }
+            }
+        }
+        return cutPoints;
+    }
 }