Implemented DDLUTILS-184
Created enum for the cascade settings for DDUTILS-75, and added properties to the foreign key object

git-svn-id: https://svn.apache.org/repos/asf/db/ddlutils/trunk@572524 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/.classpath b/.classpath
index ab8de05..c03a842 100644
--- a/.classpath
+++ b/.classpath
@@ -34,6 +34,5 @@
 	<classpathentry kind="lib" path="lib/build-only/junit-3.8.2.jar"/>
 	<classpathentry kind="lib" path="lib/stax-api-1.0.1.jar"/>
 	<classpathentry kind="lib" path="lib/log4j-1.2.8.jar"/>
-	<classpathentry kind="lib" path="lib/commons-betwixt-0.8.jar"/>
 	<classpathentry kind="output" path="target/classes"/>
 </classpath>
diff --git a/lib/commons-betwixt-0.8.jar b/lib/commons-betwixt-0.8.jar
deleted file mode 100644
index ab00040..0000000
--- a/lib/commons-betwixt-0.8.jar
+++ /dev/null
Binary files differ
diff --git a/src/java/org/apache/ddlutils/io/DataWriter.java b/src/java/org/apache/ddlutils/io/DataWriter.java
index 8d5d9d6..b90b06c 100644
--- a/src/java/org/apache/ddlutils/io/DataWriter.java
+++ b/src/java/org/apache/ddlutils/io/DataWriter.java
@@ -20,7 +20,6 @@
  */

 

 import java.io.OutputStream;

-import java.io.PrintWriter;

 import java.io.Writer;

 import java.util.ArrayList;

 import java.util.Collection;

@@ -29,10 +28,6 @@
 import java.util.List;

 import java.util.Map;

 

-import javax.xml.stream.XMLOutputFactory;

-import javax.xml.stream.XMLStreamException;

-import javax.xml.stream.XMLStreamWriter;

-

 import org.apache.commons.beanutils.DynaBean;

 import org.apache.commons.codec.binary.Base64;

 import org.apache.commons.logging.Log;

@@ -51,27 +46,17 @@
  * 

  * @version $Revision: 289996 $

  */

-public class DataWriter

+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;

-    /** The indentation string. */

-    private static final String INDENT_STRING = "  ";

 

     /** Our log. */

     private final Log _log = LogFactory.getLog(DataWriter.class);

 

     /** The converters. */

     private ConverterConfiguration _converterConf = new ConverterConfiguration();

-    /** The output stream. */

-    private PrintWriter _output;

-    /** The xml writer. */

-    private XMLStreamWriter _writer;

-    /** The output encoding. */

-    private String _encoding;

-    /** Whether we're pretty-printing. */

-    private boolean _prettyPrinting = true;

 

     /**

      * Creates a data writer instance using UTF-8 encoding.

@@ -91,26 +76,7 @@
      */

     public DataWriter(OutputStream output, String encoding) throws DataWriterException

     {

-        _output = new PrintWriter(output);

-        if ((encoding == null) || (encoding.length() == 0))

-        {

-            _encoding = "UTF-8";

-        }

-        else

-        {

-            _encoding = encoding;

-        }

-

-        try

-        {

-            XMLOutputFactory factory = XMLOutputFactory.newInstance();

-

-            _writer  = factory.createXMLStreamWriter(output, _encoding);

-        }

-        catch (XMLStreamException ex)

-        {

-            throw new DataWriterException(ex);

-        }

+        super(output, encoding);

     }

 

     /**

@@ -122,38 +88,15 @@
      */

     public DataWriter(Writer output, String encoding) throws DataWriterException

     {

-        _output   = new PrintWriter(output);

-        _encoding = encoding;

-        try

-        {

-            XMLOutputFactory factory = XMLOutputFactory.newInstance();

-

-            _writer = factory.createXMLStreamWriter(_output);

-        }

-        catch (XMLStreamException ex)

-        {

-            throw new DataWriterException(ex);

-        }

+        super(output, encoding);

     }

 

     /**

-     * Determines whether the output shall be pretty-printed.

-     *

-     * @return <code>true</code> if the output is pretty-printed

+     * {@inheritDoc}

      */

-    public boolean isPrettyPrinting()

+    protected void throwException(Exception baseEx) throws DdlUtilsXMLException

     {

-        return _prettyPrinting;

-    }

-

-    /**

-     * Specifies whether the output shall be pretty-printed.

-     *

-     * @param prettyPrinting <code>true</code> if the output is pretty-printed

-     */

-    public void setPrettyPrinting(boolean prettyPrinting)

-    {

-        _prettyPrinting = prettyPrinting;

+        throw new DataWriterException(baseEx);

     }

 

     /**

@@ -167,83 +110,25 @@
     }

 

     /**

-     * Prints a newline if we're pretty-printing.

+     * Writes the start of the XML document, including the start of the outermost

+     * XML element (<code>data</code>).

      */

-    private void printlnIfPrettyPrinting() throws DataWriterException

+    public void writeDocumentStart() throws DdlUtilsXMLException

     {

-        if (_prettyPrinting)

-        {

-            try

-            {

-                _writer.writeCharacters("\n");

-            }

-            catch (XMLStreamException ex)

-            {

-                throw new DataWriterException(ex);

-            }

-        }

+        super.writeDocumentStart();

+        writeElementStart(null, "data");

+        printlnIfPrettyPrinting();

     }

 

     /**

-     * Prints the indentation if we're pretty-printing.

-     * 

-     * @param level The indentation level

+     * Writes the end of the XML document, including the end of the outermost

+     * XML element (<code>data</code>).

      */

-    private void indentIfPrettyPrinting(int level) throws DataWriterException

+    public void writeDocumentEnd() throws DdlUtilsXMLException

     {

-        if (_prettyPrinting)

-        {

-            try

-            {

-                for (int idx = 0; idx < level; idx++)

-                {

-                    _writer.writeCharacters(INDENT_STRING);

-                }

-            }

-            catch (XMLStreamException ex)

-            {

-                throw new DataWriterException(ex);

-            }

-        }

-    }

-

-    /**

-     * Writes the start of the XML document, i.e. the "<?xml?>" section and the start of the

-     * root node.

-     */

-    public void writeDocumentStart() throws DataWriterException

-    {

-        try

-        {

-            _writer.writeStartDocument(_encoding, "1.0");

-            printlnIfPrettyPrinting();

-            _writer.writeStartElement("data");

-            printlnIfPrettyPrinting();

-        }

-        catch (XMLStreamException ex)

-        {

-            throw new DataWriterException(ex);

-        }

-    }

-

-    /**

-     * Writes the end of the XML document, i.e. end of the root node.

-     */

-    public void writeDocumentEnd() throws DataWriterException

-    {

-        try

-        {

-            _writer.writeEndElement();

-            printlnIfPrettyPrinting();

-            _writer.writeEndDocument();

-            _writer.flush();

-            _writer.close();

-            _output.close();

-        }

-        catch (XMLStreamException ex)

-        {

-            throw new DataWriterException(ex);

-        }

+        writeElementEnd();

+        printlnIfPrettyPrinting();

+        super.writeDocumentEnd();

     }

 

     /**

@@ -260,7 +145,7 @@
         try

         {

             indentIfPrettyPrinting(1);

-            _writer.writeStartElement(table.getName());

+            writeElementStart(null, table.getName());

             for (int idx = 0; idx < table.getColumnCount(); idx++)

             {

                 Column           column      = table.getColumn(idx);

@@ -290,7 +175,7 @@
                     }

                     else

                     {

-                        _writer.writeAttribute(column.getName(), valueAsText);

+                        writeAttribute(null, column.getName(), valueAsText);

                     }

                 }

             }

@@ -305,7 +190,7 @@
 

                     printlnIfPrettyPrinting();

                     indentIfPrettyPrinting(2);

-                    _writer.writeStartElement(entry.getKey().toString());

+                    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

@@ -316,14 +201,14 @@
 

                     if (writeBase64Encoded)

                     {

-                        _writer.writeAttribute(DatabaseIO.BASE64_ATTR_NAME, "true");

-                        _writer.writeCData(new String(Base64.encodeBase64(content.getBytes())));

+                        writeAttribute(null, DatabaseIO.BASE64_ATTR_NAME, "true");

+                        writeCData(new String(Base64.encodeBase64(content.getBytes())));

                     }

                     else

                     {

                         if (cutPoints.isEmpty())

                         {

-                            _writer.writeCData(content);

+                            writeCData(content);

                         }

                         else

                         {

@@ -333,28 +218,24 @@
                             {

                                 int curPos = ((Integer)cutPointIt.next()).intValue();

 

-                                _writer.writeCData(content.substring(lastPos, curPos));

+                                writeCData(content.substring(lastPos, curPos));

                                 lastPos = curPos;

                             }

                             if (lastPos < content.length())

                             {

-                                _writer.writeCData(content.substring(lastPos));

+                                writeCData(content.substring(lastPos));

                             }

                         }

                     }

 

-                    _writer.writeEndElement();

+                    writeElementEnd();

                 }

                 printlnIfPrettyPrinting();

                 indentIfPrettyPrinting(1);

             }

-            _writer.writeEndElement();

+            writeElementEnd();

             printlnIfPrettyPrinting();

         }

-        catch (XMLStreamException ex)

-        {

-            throw new DataWriterException(ex);

-        }

         catch (ConversionException ex)

         {

             throw new DataWriterException(ex);

diff --git a/src/java/org/apache/ddlutils/io/DataWriterException.java b/src/java/org/apache/ddlutils/io/DataWriterException.java
index 4294fa1..e29bff2 100644
--- a/src/java/org/apache/ddlutils/io/DataWriterException.java
+++ b/src/java/org/apache/ddlutils/io/DataWriterException.java
@@ -19,14 +19,13 @@
  * under the License.

  */

 

-import org.apache.commons.lang.exception.NestableRuntimeException;

 

 /**

  * Exception generated by the {@link org.apache.ddlutils.io.DataWriter}.

  * 

  * @version $Revision: 289996 $

  */

-public class DataWriterException extends NestableRuntimeException

+public class DataWriterException extends DdlUtilsXMLException

 {

     /** Unique id for serialization purposes. */

     private static final long serialVersionUID = 6254759931565130848L;

diff --git a/src/java/org/apache/ddlutils/io/DatabaseIO.java b/src/java/org/apache/ddlutils/io/DatabaseIO.java
index d1557c6..5e296b9 100644
--- a/src/java/org/apache/ddlutils/io/DatabaseIO.java
+++ b/src/java/org/apache/ddlutils/io/DatabaseIO.java
@@ -19,9 +19,9 @@
  * under the License.
  */
 
-import java.beans.IntrospectionException;
 import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -29,13 +29,23 @@
 import java.io.Reader;
 import java.io.Writer;
 
-import org.apache.commons.betwixt.io.BeanReader;
-import org.apache.commons.betwixt.io.BeanWriter;
-import org.apache.commons.betwixt.strategy.HyphenatedNameMapper;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.commons.lang.StringUtils;
 import org.apache.ddlutils.DdlUtilsException;
+import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.model.ForeignKey;
+import org.apache.ddlutils.model.Index;
+import org.apache.ddlutils.model.IndexColumn;
+import org.apache.ddlutils.model.NonUniqueIndex;
+import org.apache.ddlutils.model.Reference;
+import org.apache.ddlutils.model.Table;
+import org.apache.ddlutils.model.UniqueIndex;
 import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
 
 /**
  * This class provides functions to read and write database models from/to XML.
@@ -48,11 +58,62 @@
         element uses Base64 encoding. */
     public static final String BASE64_ATTR_NAME = "base64";
 
+    /** The namespace used by DdlUtils. */
+    public static final String DDLUTILS_NAMESPACE = "http://db.apache.org/torque/";
+
+    /** Qualified name of the column element. */
+    public static final QName QNAME_ELEMENT_COLUMN        = new QName(DDLUTILS_NAMESPACE, "column");
+    /** Qualified name of the database element. */
+    public static final QName QNAME_ELEMENT_DATABASE      = new QName(DDLUTILS_NAMESPACE, "database");
+    /** Qualified name of the foreign-key element. */
+    public static final QName QNAME_ELEMENT_FOREIGN_KEY   = new QName(DDLUTILS_NAMESPACE, "foreign-key");
+    /** Qualified name of the index element. */
+    public static final QName QNAME_ELEMENT_INDEX         = new QName(DDLUTILS_NAMESPACE, "index");
+    /** Qualified name of the index-column element. */
+    public static final QName QNAME_ELEMENT_INDEX_COLUMN  = new QName(DDLUTILS_NAMESPACE, "index-column");
+    /** Qualified name of the reference element. */
+    public static final QName QNAME_ELEMENT_REFERENCE     = new QName(DDLUTILS_NAMESPACE, "reference");
+    /** Qualified name of the table element. */
+    public static final QName QNAME_ELEMENT_TABLE         = new QName(DDLUTILS_NAMESPACE, "table");
+    /** Qualified name of the unique element. */
+    public static final QName QNAME_ELEMENT_UNIQUE        = new QName(DDLUTILS_NAMESPACE, "unique");
+    /** Qualified name of the unique-column element. */
+    public static final QName QNAME_ELEMENT_UNIQUE_COLUMN = new QName(DDLUTILS_NAMESPACE, "unique-column");
+
+    /** Qualified name of the autoIncrement attribute. */
+    public static final QName QNAME_ATTRIBUTE_AUTO_INCREMENT    = new QName(DDLUTILS_NAMESPACE, "autoIncrement");
+    /** Qualified name of the default attribute. */
+    public static final QName QNAME_ATTRIBUTE_DEFAULT           = new QName(DDLUTILS_NAMESPACE, "default");
+    /** Qualified name of the defaultIdMethod attribute. */
+    public static final QName QNAME_ATTRIBUTE_DEFAULT_ID_METHOD = new QName(DDLUTILS_NAMESPACE, "defaultIdMethod");
+    /** Qualified name of the description attribute. */
+    public static final QName QNAME_ATTRIBUTE_DESCRIPTION       = new QName(DDLUTILS_NAMESPACE, "description");
+    /** Qualified name of the foreign attribute. */
+    public static final QName QNAME_ATTRIBUTE_FOREIGN           = new QName(DDLUTILS_NAMESPACE, "foreign");
+    /** Qualified name of the foreignTable attribute. */
+    public static final QName QNAME_ATTRIBUTE_FOREIGN_TABLE     = new QName(DDLUTILS_NAMESPACE, "foreignTable");
+    /** Qualified name of the javaName attribute. */
+    public static final QName QNAME_ATTRIBUTE_JAVA_NAME         = new QName(DDLUTILS_NAMESPACE, "javaName");
+    /** Qualified name of the local attribute. */
+    public static final QName QNAME_ATTRIBUTE_LOCAL             = new QName(DDLUTILS_NAMESPACE, "local");
+    /** Qualified name of the name attribute. */
+    public static final QName QNAME_ATTRIBUTE_NAME              = new QName(DDLUTILS_NAMESPACE, "name");
+    /** Qualified name of the primaryKey attribute. */
+    public static final QName QNAME_ATTRIBUTE_PRIMARY_KEY       = new QName(DDLUTILS_NAMESPACE, "primaryKey");
+    /** Qualified name of the required attribute. */
+    public static final QName QNAME_ATTRIBUTE_REQUIRED          = new QName(DDLUTILS_NAMESPACE, "required");
+    /** Qualified name of the size attribute. */
+    public static final QName QNAME_ATTRIBUTE_SIZE              = new QName(DDLUTILS_NAMESPACE, "size");
+    /** Qualified name of the type attribute. */
+    public static final QName QNAME_ATTRIBUTE_TYPE              = new QName(DDLUTILS_NAMESPACE, "type");
+    /** Qualified name of the version attribute. */
+    public static final QName QNAME_ATTRIBUTE_VERSION           = new QName(DDLUTILS_NAMESPACE, "version");
+
     /** Whether to validate the XML. */
     private boolean _validateXml = true;
     /** Whether to use the internal dtd that comes with DdlUtils. */
     private boolean _useInternalDtd = true;
-
+    
     /**
      * Returns whether XML is validated upon reading it.
      * 
@@ -94,66 +155,6 @@
     }
 
     /**
-     * Returns the commons-betwixt mapping file as an {@link org.xml.sax.InputSource} object.
-     * Per default, this will be classpath resource under the path <code>/mapping.xml</code>.
-     *  
-     * @return The input source for the mapping
-     */
-    protected InputSource getBetwixtMapping()
-    {
-        return new InputSource(getClass().getResourceAsStream("/mapping.xml"));
-    }
-    
-    /**
-     * Returns a new bean reader configured to read database models.
-     * 
-     * @return The reader
-     */
-    protected BeanReader getReader() throws IntrospectionException, SAXException, IOException
-    {
-        BeanReader reader = new BeanReader();
-
-        reader.getXMLIntrospector().getConfiguration().setAttributesForPrimitives(true);
-        reader.getXMLIntrospector().getConfiguration().setWrapCollectionsInElement(false);
-        reader.getXMLIntrospector().getConfiguration().setElementNameMapper(new HyphenatedNameMapper());
-        reader.setValidating(isValidateXml());
-        if (isUseInternalDtd())
-        {
-            reader.setEntityResolver(new LocalEntityResolver());
-        }
-        reader.registerMultiMapping(getBetwixtMapping());
-
-        return reader;
-    }
-
-    /**
-     * Returns a new bean writer configured to writer database models.
-     * 
-     * @param output The target output writer
-     * @return The writer
-     */
-    protected BeanWriter getWriter(Writer output) throws DdlUtilsException
-    {
-        try
-        {
-            BeanWriter writer = new BeanWriter(output);
-    
-            writer.getXMLIntrospector().register(getBetwixtMapping());
-            writer.getXMLIntrospector().getConfiguration().setAttributesForPrimitives(true);
-            writer.getXMLIntrospector().getConfiguration().setWrapCollectionsInElement(false);
-            writer.getXMLIntrospector().getConfiguration().setElementNameMapper(new HyphenatedNameMapper());
-            writer.getBindingConfiguration().setMapIDs(false);
-            writer.enablePrettyPrint();
-    
-            return writer;
-        }
-        catch (Exception ex)
-        {
-            throw new DdlUtilsException(ex);
-        }
-    }
-
-    /**
      * Reads the database model contained in the specified file.
      * 
      * @param filename The model file name
@@ -161,18 +162,14 @@
      */
     public Database read(String filename) throws DdlUtilsException
     {
-        Database model = null;
-
         try
         {
-            model = (Database)getReader().parse(filename);
+            return read(new FileReader(filename));
         }
-        catch (Exception ex)
+        catch (IOException ex)
         {
             throw new DdlUtilsException(ex);
         }
-        model.initialize();
-        return model;
     }
 
     /**
@@ -183,18 +180,14 @@
      */
     public Database read(File file) throws DdlUtilsException
     {
-        Database model = null;
-
         try
         {
-            model = (Database)getReader().parse(file);
+            return read(new FileReader(file));
         }
-        catch (Exception ex)
+        catch (IOException ex)
         {
             throw new DdlUtilsException(ex);
         }
-        model.initialize();
-        return model;
     }
 
     /**
@@ -205,18 +198,14 @@
      */
     public Database read(Reader reader) throws DdlUtilsException
     {
-        Database model = null;
-
         try
         {
-            model = (Database)getReader().parse(reader);
+            return read(getXMLInputFactory().createXMLStreamReader(reader));
         }
-        catch (Exception ex)
+        catch (XMLStreamException ex)
         {
             throw new DdlUtilsException(ex);
         }
-        model.initialize();
-        return model;
     }
 
     /**
@@ -227,27 +216,563 @@
      */
     public Database read(InputSource source) throws DdlUtilsException
     {
+        try
+        {
+            return read(getXMLInputFactory().createXMLStreamReader(source.getCharacterStream()));
+        }
+        catch (XMLStreamException ex)
+        {
+            throw new DdlUtilsException(ex);
+        }
+    }
+
+    /**
+     * Creates a new, initialized XML input factory object.
+     * 
+     * @return The factory object
+     */
+    private XMLInputFactory getXMLInputFactory()
+    {
+        XMLInputFactory factory = XMLInputFactory.newInstance();
+
+        factory.setProperty("javax.xml.stream.isCoalescing",     Boolean.TRUE);
+        factory.setProperty("javax.xml.stream.isNamespaceAware", Boolean.TRUE);
+        return factory;
+    }
+
+    /**
+     * Reads the database model from the given XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The database model
+     */
+    private Database read(XMLStreamReader xmlReader) throws DdlUtilsException
+    {
         Database model = null;
 
         try
         {
-            model = (Database)getReader().parse(source);
+            while (xmlReader.getEventType() != XMLStreamReader.START_ELEMENT)
+            {
+                if (xmlReader.next() == XMLStreamReader.END_DOCUMENT)
+                {
+                    return null;
+                }
+            }
+            if (isSameAs(xmlReader.getName(), QNAME_ELEMENT_DATABASE))
+            {
+                model = readDatabaseElement(xmlReader);
+            }
         }
-        catch (Exception ex)
+        catch (IOException ex)
         {
             throw new DdlUtilsException(ex);
         }
-        model.initialize();
+        catch (XMLStreamException ex)
+        {
+            throw new DdlUtilsException(ex);
+        }
+        if (model != null)
+        {
+            model.initialize();
+        }
         return model;
     }
 
     /**
+     * Reads a database element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The database object
+     */
+    private Database readDatabaseElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        Database model = new Database();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                model.setName(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_DEFAULT_ID_METHOD))
+            {
+                model.setIdMethod(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_VERSION))
+            {
+                model.setVersion(xmlReader.getAttributeValue(idx));
+            }
+        }
+        readTableElements(xmlReader, model);
+        consumeRestOfElement(xmlReader);
+        return model;
+    }
+
+    /**
+     * Reads table elements from the XML stream reader and adds them to the given
+     * database model.
+     * 
+     * @param xmlReader The reader
+     * @param model     The database model to add the table objects to
+     */
+    private void readTableElements(XMLStreamReader xmlReader, Database model) throws XMLStreamException, IOException
+    {
+        int eventType = XMLStreamReader.START_ELEMENT;
+
+        while (eventType != XMLStreamReader.END_ELEMENT)
+        {
+            eventType = xmlReader.nextTag();
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                if (isSameAs(xmlReader.getName(), QNAME_ELEMENT_TABLE)) {
+                    model.addTable(readTableElement(xmlReader));
+                }
+                else {
+                    readOverElement(xmlReader);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads a table element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The table object
+     */
+    private Table readTableElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        Table table = new Table();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                table.setName(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_DESCRIPTION))
+            {
+                table.setDescription(xmlReader.getAttributeValue(idx));
+            }
+        }
+        readTableSubElements(xmlReader, table);
+        consumeRestOfElement(xmlReader);
+        return table;
+    }
+
+    /**
+     * Reads table sub elements (column, foreign key, index) from the XML stream reader and adds
+     * them to the given table.
+     * 
+     * @param xmlReader The reader
+     * @param table     The table
+     */
+    private void readTableSubElements(XMLStreamReader xmlReader, Table table) throws XMLStreamException, IOException
+    {
+        int eventType = XMLStreamReader.START_ELEMENT;
+
+        while (eventType != XMLStreamReader.END_ELEMENT)
+        {
+            eventType = xmlReader.nextTag();
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                QName elemQName = xmlReader.getName();
+
+                if (isSameAs(elemQName, QNAME_ELEMENT_COLUMN))
+                {
+                    table.addColumn(readColumnElement(xmlReader));
+                }
+                else if (isSameAs(elemQName, QNAME_ELEMENT_FOREIGN_KEY))
+                {
+                    table.addForeignKey(readForeignKeyElement(xmlReader));
+                }
+                else if (isSameAs(elemQName, QNAME_ELEMENT_INDEX))
+                {
+                    table.addIndex(readIndexElement(xmlReader));
+                }
+                else if (isSameAs(elemQName, QNAME_ELEMENT_UNIQUE))
+                {
+                    table.addIndex(readUniqueElement(xmlReader));
+                }
+                else {
+                    readOverElement(xmlReader);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads a column element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The column object
+     */
+    private Column readColumnElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        Column column = new Column();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                column.setName(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_PRIMARY_KEY))
+            {
+                column.setPrimaryKey(getAttributeValueAsBoolean(xmlReader, idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_REQUIRED))
+            {
+                column.setRequired(getAttributeValueAsBoolean(xmlReader, idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_TYPE))
+            {
+                column.setType(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_SIZE))
+            {
+                column.setSize(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_DEFAULT))
+            {
+                column.setDefaultValue(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_AUTO_INCREMENT))
+            {
+                column.setAutoIncrement(getAttributeValueAsBoolean(xmlReader, idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_DESCRIPTION))
+            {
+                column.setDescription(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_JAVA_NAME))
+            {
+                column.setJavaName(xmlReader.getAttributeValue(idx));
+            }
+        }
+        consumeRestOfElement(xmlReader);
+        return column;
+    }
+
+    /**
+     * Reads a foreign key element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The foreign key object
+     */
+    private ForeignKey readForeignKeyElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        ForeignKey foreignKey = new ForeignKey();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_FOREIGN_TABLE))
+            {
+                foreignKey.setForeignTableName(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                foreignKey.setName(xmlReader.getAttributeValue(idx));
+            }
+        }
+        readReferenceElements(xmlReader, foreignKey);
+        consumeRestOfElement(xmlReader);
+        return foreignKey;
+    }
+
+    /**
+     * Reads reference elements from the XML stream reader and adds them to the given
+     * foreign key.
+     * 
+     * @param xmlReader  The reader
+     * @param foreignKey The foreign key
+     */
+    private void readReferenceElements(XMLStreamReader xmlReader, ForeignKey foreignKey) throws XMLStreamException, IOException
+    {
+        int eventType = XMLStreamReader.START_ELEMENT;
+
+        while (eventType != XMLStreamReader.END_ELEMENT)
+        {
+            eventType = xmlReader.nextTag();
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                QName elemQName = xmlReader.getName();
+
+                if (isSameAs(elemQName, QNAME_ELEMENT_REFERENCE))
+                {
+                    foreignKey.addReference(readReferenceElement(xmlReader));
+                }
+                else {
+                    readOverElement(xmlReader);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads a reference element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The reference object
+     */
+    private Reference readReferenceElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        Reference reference = new Reference();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_LOCAL))
+            {
+                reference.setLocalColumnName(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_FOREIGN))
+            {
+                reference.setForeignColumnName(xmlReader.getAttributeValue(idx));
+            }
+        }
+        consumeRestOfElement(xmlReader);
+        return reference;
+    }
+
+    /**
+     * Reads an index element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The index object
+     */
+    private Index readIndexElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        Index index = new NonUniqueIndex();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                index.setName(xmlReader.getAttributeValue(idx));
+            }
+        }
+        readIndexColumnElements(xmlReader, index);
+        consumeRestOfElement(xmlReader);
+        return index;
+    }
+
+    /**
+     * Reads an unique index element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The unique index object
+     */
+    private Index readUniqueElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        Index index = new UniqueIndex();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                index.setName(xmlReader.getAttributeValue(idx));
+            }
+        }
+        readUniqueColumnElements(xmlReader, index);
+        consumeRestOfElement(xmlReader);
+        return index;
+    }
+
+    /**
+     * Reads index column elements from the XML stream reader and adds them to the given
+     * index object.
+     * 
+     * @param xmlReader The reader
+     * @param index     The index object
+     */
+    private void readIndexColumnElements(XMLStreamReader xmlReader, Index index) throws XMLStreamException, IOException
+    {
+        int eventType = XMLStreamReader.START_ELEMENT;
+
+        while (eventType != XMLStreamReader.END_ELEMENT)
+        {
+            eventType = xmlReader.nextTag();
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                QName elemQName = xmlReader.getName();
+
+                if (isSameAs(elemQName, QNAME_ELEMENT_INDEX_COLUMN))
+                {
+                    index.addColumn(readIndexColumnElement(xmlReader));
+                }
+                else {
+                    readOverElement(xmlReader);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads unique index column elements from the XML stream reader and adds them to the given
+     * index object.
+     * 
+     * @param xmlReader The reader
+     * @param index     The index object
+     */
+    private void readUniqueColumnElements(XMLStreamReader xmlReader, Index index) throws XMLStreamException, IOException
+    {
+        int eventType = XMLStreamReader.START_ELEMENT;
+
+        while (eventType != XMLStreamReader.END_ELEMENT)
+        {
+            eventType = xmlReader.nextTag();
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                QName elemQName = xmlReader.getName();
+
+                if (isSameAs(elemQName, QNAME_ELEMENT_UNIQUE_COLUMN))
+                {
+                    index.addColumn(readIndexColumnElement(xmlReader));
+                }
+                else {
+                    readOverElement(xmlReader);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads an index column element from the XML stream reader.
+     * 
+     * @param xmlReader The reader
+     * @return The index column object
+     */
+    private IndexColumn readIndexColumnElement(XMLStreamReader xmlReader) throws XMLStreamException, IOException
+    {
+        IndexColumn indexColumn = new IndexColumn();
+
+        for (int idx = 0; idx < xmlReader.getAttributeCount(); idx++)
+        {
+            QName attrQName = xmlReader.getAttributeName(idx);
+
+            if (isSameAs(attrQName, QNAME_ATTRIBUTE_NAME))
+            {
+                indexColumn.setName(xmlReader.getAttributeValue(idx));
+            }
+            else if (isSameAs(attrQName, QNAME_ATTRIBUTE_SIZE))
+            {
+                indexColumn.setSize(xmlReader.getAttributeValue(idx));
+            }
+        }
+        consumeRestOfElement(xmlReader);
+        return indexColumn;
+    }
+
+    /**
+     * Compares the given qnames. This specifically ignores the namespace
+     * uri of the other qname if the namespace of the current element is
+     * empty.
+     * 
+     * @param curElemQName The qname of the current element
+     * @param qName        The qname to compare to
+     * @return <code>true</code> if they are the same
+     */
+    private boolean isSameAs(QName curElemQName, QName qName)
+    {
+        if (StringUtils.isEmpty(curElemQName.getNamespaceURI()))
+        {
+            return qName.getLocalPart().equals(curElemQName.getLocalPart());
+        }
+        else
+        {
+            return qName.equals(curElemQName);
+        }
+    }
+
+    /**
+     * Returns the value of the indicated attribute of the current element as a boolean.
+     * If the value is not a valid boolean, then an exception is thrown.
+     * 
+     * @param xmlReader    The xml reader
+     * @param attributeIdx The index of the attribute
+     * @return The attribute's value as a boolean
+     */
+    private boolean getAttributeValueAsBoolean(XMLStreamReader xmlReader, int attributeIdx) throws DdlUtilsException
+    {
+        String value = xmlReader.getAttributeValue(attributeIdx);
+
+        if ("true".equalsIgnoreCase(value))
+        {
+            return true;
+        }
+        else if ("false".equalsIgnoreCase(value))
+        {
+            return false;
+        }
+        else
+        {
+            throw new DdlUtilsException("Illegal boolean value '" + value +"' for attribute " + xmlReader.getAttributeLocalName(attributeIdx));
+        }
+    }
+
+    /**
+     * Consumes the rest of the current element. This assumes that the current XML stream
+     * event type is not START_ELEMENT.
+     * 
+     * @param reader The xml reader
+     */
+    private void consumeRestOfElement(XMLStreamReader reader) throws XMLStreamException
+    {
+        int eventType = reader.getEventType();
+
+        while ((eventType != XMLStreamReader.END_ELEMENT) && (eventType != XMLStreamReader.END_DOCUMENT))
+        {
+            eventType = reader.nextTag();
+        }
+    }
+
+    /**
+     * 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.nextTag();
+
+            if (eventType == XMLStreamReader.START_ELEMENT)
+            {
+                depth++;
+            }
+            else if (eventType == XMLStreamReader.END_ELEMENT)
+            {
+                depth--;
+            }
+        }
+    }
+
+    /**
      * Writes the database model to the specified file.
      * 
      * @param model    The database model
      * @param filename The model file name
      */
-    public void write(Database model, String filename) throws DdlUtilsException
+    public void write(Database model, String filename) throws DdlUtilsXMLException
     {
         try
         {
@@ -276,44 +801,222 @@
 
     /**
      * Writes the database model to the given output stream. Note that this method
-     * does not flush the stream.
+     * does not flush or close the stream.
      * 
      * @param model  The database model
      * @param output The output stream
      */
-    public void write(Database model, OutputStream output) throws DdlUtilsException
+    public void write(Database model, OutputStream output) throws DdlUtilsXMLException
     {
-        write(model, getWriter(new OutputStreamWriter(output)));
+        write(model, new OutputStreamWriter(output));
     }
 
     /**
      * Writes the database model to the given output writer. Note that this method
-     * does not flush the writer.
+     * does not flush or close the writer.
      * 
      * @param model  The database model
      * @param output The output writer
      */
-    public void write(Database model, Writer output) throws DdlUtilsException
+    public void write(Database model, Writer output) throws DdlUtilsXMLException
     {
-        write(model, getWriter(output));
+        PrettyPrintingXmlWriter xmlWriter = new PrettyPrintingXmlWriter(output, "UTF-8");
+
+        xmlWriter.setDefaultNamespace(DDLUTILS_NAMESPACE);
+        xmlWriter.writeDocumentStart();
+        writeDatabaseElement(model, xmlWriter);
+        xmlWriter.writeDocumentEnd();
     }
 
     /**
-     * Internal method that writes the database model using the given bean writer.
+     * Writes the database model to the given XML writer.
      * 
-     * @param model  The database model
-     * @param writer The bean writer
+     * @param model     The database model
+     * @param xmlWriter The XML writer
      */
-    private void write(Database model, BeanWriter writer) throws DdlUtilsException
+    private void writeDatabaseElement(Database model, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
     {
-        try
+        writeElementStart(xmlWriter, QNAME_ELEMENT_DATABASE);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_NAME,              model.getName());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_DEFAULT_ID_METHOD, model.getIdMethod());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_VERSION,           model.getVersion());
+        if (model.getTableCount() > 0)
         {
-            writer.writeXmlDeclaration("<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">");
-            writer.write(model);
+            xmlWriter.printlnIfPrettyPrinting();
+            for (int idx = 0; idx < model.getTableCount(); idx++)
+            {
+                writeTableElement(model.getTable(idx), xmlWriter);
+            }
         }
-        catch (Exception ex)
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the table object to the given XML writer.
+     * 
+     * @param table     The table object
+     * @param xmlWriter The XML writer
+     */
+    private void writeTableElement(Table table, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.indentIfPrettyPrinting(1);
+        writeElementStart(xmlWriter, QNAME_ELEMENT_TABLE);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_NAME,        table.getName());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_DESCRIPTION, table.getDescription());
+        if ((table.getColumnCount() > 0) || (table.getForeignKeyCount() > 0) || (table.getIndexCount() > 0))
         {
-            throw new DdlUtilsException(ex);
+            xmlWriter.printlnIfPrettyPrinting();
+            for (int idx = 0; idx < table.getColumnCount(); idx++)
+            {
+                writeColumnElement(table.getColumn(idx), xmlWriter);
+            }
+            for (int idx = 0; idx < table.getForeignKeyCount(); idx++)
+            {
+                writeForeignKeyElement(table.getForeignKey(idx), xmlWriter);
+            }
+            for (int idx = 0; idx < table.getIndexCount(); idx++)
+            {
+                writeIndexElement(table.getIndex(idx), xmlWriter);
+            }
+            xmlWriter.indentIfPrettyPrinting(1);
         }
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the column object to the given XML writer.
+     * 
+     * @param column    The column object
+     * @param xmlWriter The XML writer
+     */
+    private void writeColumnElement(Column column, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.indentIfPrettyPrinting(2);
+        writeElementStart(xmlWriter, QNAME_ELEMENT_COLUMN);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_NAME,           column.getName());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_PRIMARY_KEY,    String.valueOf(column.isPrimaryKey()));
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_REQUIRED,       String.valueOf(column.isRequired()));
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_TYPE,           column.getType());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_SIZE,           column.getSize());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_DEFAULT,        column.getDefaultValue());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_AUTO_INCREMENT, String.valueOf(column.isAutoIncrement()));
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_DESCRIPTION,    column.getDescription());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_JAVA_NAME,      column.getJavaName());
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the foreign key object to the given XML writer.
+     * 
+     * @param foreignKey The foreign key object
+     * @param xmlWriter  The XML writer
+     */
+    private void writeForeignKeyElement(ForeignKey foreignKey, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.indentIfPrettyPrinting(2);
+        writeElementStart(xmlWriter, QNAME_ELEMENT_FOREIGN_KEY);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_FOREIGN_TABLE, foreignKey.getForeignTableName());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_NAME,          foreignKey.getName());
+        if (foreignKey.getReferenceCount() > 0)
+        {
+            xmlWriter.printlnIfPrettyPrinting();
+            for (int idx = 0; idx < foreignKey.getReferenceCount(); idx++)
+            {
+                writeReferenceElement(foreignKey.getReference(idx), xmlWriter);
+            }
+            xmlWriter.indentIfPrettyPrinting(2);
+        }
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the reference object to the given XML writer.
+     * 
+     * @param reference The reference object
+     * @param xmlWriter The XML writer
+     */
+    private void writeReferenceElement(Reference reference, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.indentIfPrettyPrinting(3);
+        writeElementStart(xmlWriter, QNAME_ELEMENT_REFERENCE);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_LOCAL,   reference.getLocalColumnName());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_FOREIGN, reference.getForeignColumnName());
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the index object to the given XML writer.
+     * 
+     * @param index     The index object
+     * @param xmlWriter The XML writer
+     */
+    private void writeIndexElement(Index index, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.indentIfPrettyPrinting(2);
+        writeElementStart(xmlWriter, index.isUnique() ? QNAME_ELEMENT_UNIQUE : QNAME_ELEMENT_INDEX);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_NAME, index.getName());
+        if (index.getColumnCount() > 0)
+        {
+            xmlWriter.printlnIfPrettyPrinting();
+            for (int idx = 0; idx < index.getColumnCount(); idx++)
+            {
+                writeIndexColumnElement(index.getColumn(idx), index.isUnique(), xmlWriter);
+            }
+            xmlWriter.indentIfPrettyPrinting(2);
+        }
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the index column object to the given XML writer.
+     * 
+     * @param indexColumn The index column object
+     * @param isUnique    Whether the index that the index column belongs to, is unique
+     * @param xmlWriter   The XML writer
+     */
+    private void writeIndexColumnElement(IndexColumn indexColumn, boolean isUnique, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.indentIfPrettyPrinting(3);
+        writeElementStart(xmlWriter, isUnique ? QNAME_ELEMENT_UNIQUE_COLUMN : QNAME_ELEMENT_INDEX_COLUMN);
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_NAME, indexColumn.getName());
+        writeAttribute(xmlWriter, QNAME_ATTRIBUTE_SIZE, indexColumn.getSize());
+        writeElementEnd(xmlWriter);
+    }
+
+    /**
+     * Writes the start of the specified XML element to the given XML writer.
+     * 
+     * @param xmlWriter The xml writer
+     * @param qName     The qname of the XML element
+     */
+    private void writeElementStart(PrettyPrintingXmlWriter xmlWriter, QName qName) throws DdlUtilsXMLException
+    {
+        xmlWriter.writeElementStart(null, qName.getLocalPart());
+    }
+
+    /**
+     * Writes an attribute to the given XML writer.
+     * 
+     * @param xmlWriter The xml writer
+     * @param qName     The qname of the attribute
+     * @param value     The value; if empty, then nothing is written
+     */
+    private void writeAttribute(PrettyPrintingXmlWriter xmlWriter, QName qName, String value) throws DdlUtilsXMLException
+    {
+        if (!StringUtils.isEmpty(value))
+        {
+            xmlWriter.writeAttribute(null, qName.getLocalPart(), value);
+        }
+    }
+
+    /**
+     * Writes the end of the current XML element to the given XML writer.
+     * 
+     * @param xmlWriter The xml writer
+     */
+    private void writeElementEnd(PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
+    {
+        xmlWriter.writeElementEnd();
+        xmlWriter.printlnIfPrettyPrinting();
     }
 }
diff --git a/src/java/org/apache/ddlutils/io/DdlUtilsXMLException.java b/src/java/org/apache/ddlutils/io/DdlUtilsXMLException.java
new file mode 100644
index 0000000..7dba914
--- /dev/null
+++ b/src/java/org/apache/ddlutils/io/DdlUtilsXMLException.java
@@ -0,0 +1,72 @@
+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.DdlUtilsException;

+

+/**

+ * Base class for exceptions generated by DdlUtils when handling XML.

+ * 

+ * @version $Revision: $

+ */

+public class DdlUtilsXMLException extends DdlUtilsException

+{

+    /** Unique id for serialization purposes. */

+    private static final long serialVersionUID = 3464139163788952051L;

+

+    /**

+     * Creates a new exception object.

+     */

+    public DdlUtilsXMLException()

+    {

+        super();

+    }

+

+    /**

+     * Creates a new exception object.

+     * 

+     * @param message The exception message

+     */

+    public DdlUtilsXMLException(String message)

+    {

+        super(message);

+    }

+

+    /**

+     * Creates a new exception object.

+     * 

+     * @param baseEx The base exception

+     */

+    public DdlUtilsXMLException(Throwable baseEx)

+    {

+        super(baseEx);

+    }

+

+    /**

+     * Creates a new exception object.

+     * 

+     * @param message The exception message

+     * @param baseEx  The base exception

+     */

+    public DdlUtilsXMLException(String message, Throwable baseEx)

+    {

+        super(message, baseEx);

+    }

+}

diff --git a/src/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java b/src/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java
new file mode 100644
index 0000000..145ba9e
--- /dev/null
+++ b/src/java/org/apache/ddlutils/io/PrettyPrintingXmlWriter.java
@@ -0,0 +1,318 @@
+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.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * Helper class that writes XML data with or without pretty printing.
+ * 
+ * @version $Revision: $
+ */
+public class PrettyPrintingXmlWriter
+{
+    /** The indentation string. */
+    private static final String INDENT_STRING = "  ";
+
+    /** The output stream. */
+    private PrintWriter _output;
+    /** The xml writer. */
+    private XMLStreamWriter _writer;
+    /** The output encoding. */
+    private String _encoding;
+    /** Whether we're pretty-printing. */
+    private boolean _prettyPrinting = true;
+
+    /**
+     * Creates a xml writer instance using UTF-8 encoding.
+     * 
+     * @param output The target to write the data XML to
+     */
+    public PrettyPrintingXmlWriter(OutputStream output) throws DdlUtilsXMLException
+    {
+        this(output, null);
+    }
+
+    /**
+     * Creates a xml writer instance.
+     * 
+     * @param output   The target to write the data XML to
+     * @param encoding The encoding of the XML file
+     */
+    public PrettyPrintingXmlWriter(OutputStream output, String encoding) throws DdlUtilsXMLException
+    {
+        _output = new PrintWriter(output);
+        if ((encoding == null) || (encoding.length() == 0))
+        {
+            _encoding = "UTF-8";
+        }
+        else
+        {
+            _encoding = encoding;
+        }
+
+        try
+        {
+            XMLOutputFactory factory = XMLOutputFactory.newInstance();
+
+            _writer  = factory.createXMLStreamWriter(output, _encoding);
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Creates a xml writer instance using the specified writer. Note that the writer
+     * needs to be configured using the specified encoding.
+     * 
+     * @param output   The target to write the data XML to
+     * @param encoding The encoding of the writer
+     */
+    public PrettyPrintingXmlWriter(Writer output, String encoding) throws DdlUtilsXMLException
+    {
+        _output   = new PrintWriter(output);
+        _encoding = encoding;
+        try
+        {
+            XMLOutputFactory factory = XMLOutputFactory.newInstance();
+
+            _writer = factory.createXMLStreamWriter(_output);
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Rethrows the given exception, wrapped in a {@link DdlUtilsXMLException}. This
+     * method allows subclasses to throw their own subclasses of this exception.
+     * 
+     * @param baseEx The original exception
+     * @throws DdlUtilsXMLException The wrapped exception
+     */
+    protected void throwException(Exception baseEx) throws DdlUtilsXMLException
+    {
+        throw new DdlUtilsXMLException(baseEx);
+    }
+
+    /**
+     * Determines whether the output shall be pretty-printed.
+     *
+     * @return <code>true</code> if the output is pretty-printed
+     */
+    public boolean isPrettyPrinting()
+    {
+        return _prettyPrinting;
+    }
+
+    /**
+     * Specifies whether the output shall be pretty-printed.
+     *
+     * @param prettyPrinting <code>true</code> if the output is pretty-printed
+     */
+    public void setPrettyPrinting(boolean prettyPrinting)
+    {
+        _prettyPrinting = prettyPrinting;
+    }
+
+    /**
+     * Sets the default namespace.
+     * 
+     * @param uri The namespace uri
+     */
+    public void setDefaultNamespace(String uri) throws DdlUtilsXMLException
+    {
+        try
+        {
+            _writer.setDefaultNamespace(uri);
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+    
+    /**
+     * Prints a newline if we're pretty-printing.
+     */
+    public void printlnIfPrettyPrinting() throws DdlUtilsXMLException
+    {
+        if (_prettyPrinting)
+        {
+            try
+            {
+                _writer.writeCharacters("\n");
+            }
+            catch (XMLStreamException ex)
+            {
+                throwException(ex);
+            }
+        }
+    }
+
+    /**
+     * Prints the indentation if we're pretty-printing.
+     * 
+     * @param level The indentation level
+     */
+    public void indentIfPrettyPrinting(int level) throws DdlUtilsXMLException
+    {
+        if (_prettyPrinting)
+        {
+            try
+            {
+                for (int idx = 0; idx < level; idx++)
+                {
+                    _writer.writeCharacters(INDENT_STRING);
+                }
+            }
+            catch (XMLStreamException ex)
+            {
+                throwException(ex);
+            }
+        }
+    }
+
+    /**
+     * Writes the start of the XML document, i.e. the "<?xml?>" section and the start of the
+     * root node.
+     */
+    public void writeDocumentStart() throws DdlUtilsXMLException
+    {
+        try
+        {
+            _writer.writeStartDocument(_encoding, "1.0");
+            printlnIfPrettyPrinting();
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Writes the end of the XML document, i.e. end of the root node.
+     */
+    public void writeDocumentEnd() throws DdlUtilsXMLException
+    {
+        try
+        {
+            _writer.writeEndDocument();
+            _writer.flush();
+            _writer.close();
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Writes the start of the indicated XML element.
+     * 
+     * @param namespaceUri The namespace uri, can be <code>null</code>
+     * @param localPart    The local part of the element's qname
+     */
+    public void writeElementStart(String namespaceUri, String localPart) throws DdlUtilsXMLException
+    {
+        try
+        {
+            if (namespaceUri == null)
+            {
+                _writer.writeStartElement(localPart);
+            }
+            else
+            {
+                _writer.writeStartElement(namespaceUri, localPart);
+            }
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Writes the end of the current XML element.
+     */
+    public void writeElementEnd() throws DdlUtilsXMLException
+    {
+        try
+        {
+            _writer.writeEndElement();
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Writes an XML attribute.
+     * 
+     * @param namespaceUri The namespace uri, can be <code>null</code>
+     * @param localPart    The local part of the attribute's qname
+     * @param value        The value; if <code>null</code> then no attribute is written
+     */
+    public void writeAttribute(String namespaceUri, String localPart, String value) throws DdlUtilsXMLException
+    {
+        try
+        {
+            if (namespaceUri == null)
+            {
+                _writer.writeAttribute(localPart, value);
+            }
+            else
+            {
+                _writer.writeAttribute(namespaceUri, localPart, value);
+            }
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+
+    /**
+     * Writes a CDATA segment.
+     * 
+     * @param data The data to write
+     */
+    public void writeCData(String data) throws DdlUtilsXMLException
+    {
+        try
+        {
+            _writer.writeCData(data);
+        }
+        catch (XMLStreamException ex)
+        {
+            throwException(ex);
+        }
+    }
+}
diff --git a/src/java/org/apache/ddlutils/model/CascadeActionEnum.java b/src/java/org/apache/ddlutils/model/CascadeActionEnum.java
new file mode 100644
index 0000000..22ed94b
--- /dev/null
+++ b/src/java/org/apache/ddlutils/model/CascadeActionEnum.java
@@ -0,0 +1,125 @@
+package org.apache.ddlutils.model;
+
+/*
+ * 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 java.util.Map;
+
+import org.apache.commons.lang.enums.ValuedEnum;
+
+/**
+ * Represents the different cascade actions for {@link ForeignKey#onDelete} and
+ * {@link ForeignKey#onUdate}.
+ * 
+ * @version $Revision: $
+ */
+public class CascadeActionEnum extends ValuedEnum
+{
+    /** The integer value for the enum value for a cascading change. */
+    public static final int VALUE_CASCADE  = 1;
+    /** The integer value for the enum value for a set-null change. */
+    public static final int VALUE_SETNULL  = 2;
+    /** The integer value for the enum value for a restrict change. */
+    public static final int VALUE_RESTRICT = 3;
+    /** The integer value for the enum value for no-change. */
+    public static final int VALUE_NONE     = 4;
+
+    /** The enum value for a cascade action which directs the database to change the value
+        of local column to the new value of the referenced column when it changes. */
+    public static final CascadeActionEnum CASCADE  = new CascadeActionEnum("cascade",  VALUE_CASCADE);
+    /** The enum value for a cascade action which directs the database to set the local
+        column to null when the referenced column changes. */
+    public static final CascadeActionEnum SETNULL  = new CascadeActionEnum("setnull",  VALUE_SETNULL);
+    /** The enum value for a cascade action which directs the database to restrict the change
+        changes to the referenced column. The interpretation of this is database-dependent. */
+    public static final CascadeActionEnum RESTRICT = new CascadeActionEnum("restrict", VALUE_RESTRICT);
+    /** The enum value for a cascade action which directs the database to take do nothing
+        to the local column when the value of the referenced column changes. */
+    public static final CascadeActionEnum NONE     = new CascadeActionEnum("none",     VALUE_NONE);
+
+    /** Version id for this class as relevant for serialization. */
+    private static final long serialVersionUID = -6378050861446415790L;
+
+    /**
+     * Creates a new enum object.
+     * 
+     * @param defaultTextRep The textual representation
+     * @param value          The corresponding integer value
+     */
+    private CascadeActionEnum(String defaultTextRep, int value)
+    {
+        super(defaultTextRep, value);
+    }
+
+    /**
+     * Returns the enum value that corresponds to the given textual
+     * representation.
+     * 
+     * @param defaultTextRep The textual representation
+     * @return The enum value
+     */
+    public static CascadeActionEnum getEnum(String defaultTextRep)
+    {
+        return (CascadeActionEnum)getEnum(CascadeActionEnum.class, defaultTextRep);
+    }
+    
+    /**
+     * Returns the enum value that corresponds to the given integer
+     * representation.
+     * 
+     * @param intValue The integer value
+     * @return The enum value
+     */
+    public static CascadeActionEnum getEnum(int intValue)
+    {
+        return (CascadeActionEnum)getEnum(CascadeActionEnum.class, intValue);
+    }
+
+    /**
+     * Returns the map of enum values.
+     * 
+     * @return The map of enum values
+     */
+    public static Map getEnumMap()
+    {
+        return getEnumMap(CascadeActionEnum.class);
+    }
+
+    /**
+     * Returns a list of all enum values.
+     * 
+     * @return The list of enum values
+     */
+    public static List getEnumList()
+    {
+        return getEnumList(CascadeActionEnum.class);
+    }
+
+    /**
+     * Returns an iterator of all enum values.
+     * 
+     * @return The iterator
+     */
+    public static Iterator iterator()
+    {
+        return iterator(CascadeActionEnum.class);
+    }
+}
diff --git a/src/java/org/apache/ddlutils/model/ForeignKey.java b/src/java/org/apache/ddlutils/model/ForeignKey.java
index 3db04be..3d3fc82 100644
--- a/src/java/org/apache/ddlutils/model/ForeignKey.java
+++ b/src/java/org/apache/ddlutils/model/ForeignKey.java
@@ -39,6 +39,10 @@
     private Table _foreignTable;
     /** The name of the foreign table. */
     private String _foreignTableName;
+    /** The action to perform when the value of the referenced column changes. */
+    private CascadeActionEnum _onUpdate = CascadeActionEnum.NONE;
+    /** The action to perform when the referenced row is deleted. */
+    private CascadeActionEnum _onDelete = CascadeActionEnum.NONE;
     /** The references between local and remote columns. */
     private ListOrderedSet _references = new ListOrderedSet();
     /** Whether this foreign key has an associated auto-generated index. */
@@ -130,6 +134,56 @@
     }
 
     /**
+     * Returns the action for this foreignkey for when the referenced row is deleted.
+     * 
+     * @return The action
+     */
+    public CascadeActionEnum getOnDelete()
+    {
+        return _onDelete;
+    }
+
+    /**
+     * Sets the action for this foreignkey for when the referenced row is deleted.
+     * 
+     * @param onDelete The action
+     * @throws NullPointerException If <code>onDelete</code> is null
+     */
+    public void setOnDelete(CascadeActionEnum onDelete) throws NullPointerException
+    {
+        if (onDelete == null)
+        {
+            throw new NullPointerException("The onDelete action cannot be null");
+        }
+        _onDelete = onDelete;
+    }
+
+    /**
+     * Returns the action for this foreignkey for when the referenced row is changed.
+     * 
+     * @return The action
+     */
+    public CascadeActionEnum getOnUpdate()
+    {
+        return _onUpdate;
+    }
+
+    /**
+     * Sets the action for this foreignkey for when the referenced row is changed.
+     * 
+     * @param onUpdate The action
+     * @throws NullPointerException If <code>onUdate</code> is null
+     */
+    public void setOnUpdate(CascadeActionEnum onUpdate) throws NullPointerException
+    {
+        if (onUpdate == null)
+        {
+            throw new NullPointerException("The onUpdate action cannot be null");
+        }
+        _onUpdate = onUpdate;
+    }
+
+    /**
      * Returns the number of references.
      * 
      * @return The number of references
diff --git a/src/test/org/apache/ddlutils/io/TestDatabaseIO.java b/src/test/org/apache/ddlutils/io/TestDatabaseIO.java
index 17d56f5..e84ddff 100644
--- a/src/test/org/apache/ddlutils/io/TestDatabaseIO.java
+++ b/src/test/org/apache/ddlutils/io/TestDatabaseIO.java
@@ -22,13 +22,11 @@
 import java.io.StringReader;

 import java.io.StringWriter;

 import java.sql.Types;

-import java.util.Arrays;

 

 import junit.framework.TestCase;

 

 import org.apache.commons.lang.StringUtils;

-import org.apache.commons.logging.Log;

-import org.apache.commons.logging.LogFactory;

+import org.apache.ddlutils.model.CascadeActionEnum;

 import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.ForeignKey;

@@ -37,17 +35,15 @@
 import org.apache.ddlutils.model.ModelException;

 import org.apache.ddlutils.model.Reference;

 import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.model.TypeMap;

 

 /**

- * Tests the database reading/writing via the {@link org.apache.ddlutils.io.DatabaseIO} class.

+ * Tests the database XML reading/writing via the {@link org.apache.ddlutils.io.DatabaseIO} class.

  * 

  * @version $Revision: 289996 $

  */

 public class TestDatabaseIO extends TestCase

 {

-    /** The log for the tests. */

-    private final Log _log = LogFactory.getLog(TestDatabaseIO.class);

-

     /**

      * Reads the database model from the given string.

      * 

@@ -78,9 +74,426 @@
     }

 

     /**

-     * Tests a simple database model.

+     * Asserts the data in a table object.

+     * 

+     * @param name                    The expected name

+     * @param description             The expected description

+     * @param numColumns              The expected number of columns

+     * @param numPrimaryKeyColumns    The expected number of primary key columns

+     * @param numAutoIncrementColumns The expected number of auto increment columns

+     * @param numForeignKeys          The expected number of foreign keys

+     * @param numIndexes              The expected number of indexes

+     * @param table                   The table

      */

-    public void testSimple() throws Exception

+    private void assertEquals(String name,

+                              String description,

+                              int    numColumns,

+                              int    numPrimaryKeyColumns,

+                              int    numAutoIncrementColumns,

+                              int    numForeignKeys,

+                              int    numIndexes,

+                              Table  table)

+    {

+        assertEquals(name, table.getName());

+        assertEquals(description, table.getDescription());

+        assertEquals(numColumns, table.getColumnCount());

+        assertEquals(numPrimaryKeyColumns, table.getPrimaryKeyColumns().length);

+        assertEquals(numAutoIncrementColumns, table.getAutoIncrementColumns().length);

+        assertEquals(numForeignKeys, table.getForeignKeyCount());

+        assertEquals(numIndexes, table.getIndexCount());

+    }

+

+    /**

+     * Asserts the data in a column object.

+     * 

+     * @param name            The expected name

+     * @param typeCode        The exected JDBC type code

+     * @param size            The expected size value

+     * @param scale           The expected scale value

+     * @param defaultValue    The expected default value

+     * @param description     The expected description

+     * @param javaName        The expected java name

+     * @param isPrimaryKey    The expected primary key status

+     * @param isRequired      The expected required satus

+     * @param isAutoIncrement The expected auto increment status

+     * @param column          The column

+     */

+    private void assertEquals(String  name,

+                              int     typeCode,

+                              int     size,

+                              int     scale,

+                              String  defaultValue,

+                              String  description,

+                              String  javaName,

+                              boolean isPrimaryKey,

+                              boolean isRequired,

+                              boolean isAutoIncrement,

+                              Column  column)

+    {

+        assertEquals(name, column.getName());

+        assertEquals(TypeMap.getJdbcTypeName(typeCode), column.getType());

+        assertEquals(typeCode, column.getTypeCode());

+        assertEquals(size, column.getSizeAsInt());

+        assertEquals(size, column.getPrecisionRadix());

+        assertEquals(scale, column.getScale());

+        if ((size <= 0) && (scale <= 0)) {

+            assertNull(column.getSize());

+        }

+        else if (scale == 0) {

+            assertEquals("" + size, column.getSize());

+        }

+        else {

+            assertEquals("" + size + "," + scale, column.getSize());

+        }

+        assertEquals(defaultValue, column.getDefaultValue());

+        assertEquals(description, column.getDescription());

+        assertEquals(javaName, column.getJavaName());

+        assertEquals(isPrimaryKey, column.isPrimaryKey());

+        assertEquals(isRequired, column.isRequired());

+        assertEquals(isAutoIncrement, column.isAutoIncrement());

+    }

+

+    /**

+     * Asserts data in a foreign key object.

+     * 

+     * @param name            The expected name

+     * @param onUpdate        The expected onUpdate action

+     * @param onDelete        The expected onDelete action

+     * @param referencedTable The expected referenced table

+     * @param numReferences   The expected number of references

+     * @param foreignKey      The foreign key

+     */

+    private void assertEquals(String            name,

+                              CascadeActionEnum onUpdate,

+                              CascadeActionEnum onDelete,

+                              Table             referencedTable,

+                              int               numReferences,

+                              ForeignKey        foreignKey)

+    {

+        assertEquals(name, foreignKey.getName());

+        assertEquals(onUpdate, foreignKey.getOnUpdate());

+        assertEquals(onDelete, foreignKey.getOnDelete());

+        assertEquals(referencedTable, foreignKey.getForeignTable());

+        assertEquals(referencedTable.getName(), foreignKey.getForeignTableName());

+        assertEquals(numReferences, foreignKey.getReferenceCount());

+    }

+

+    /**

+     * Asserts data in a reference object.

+     * 

+     * @param localColumn   The expected local column

+     * @param foreignColumn The expected foreign column

+     * @param ref           The reference

+     */

+    private void assertEquals(Column localColumn, Column foreignColumn, Reference ref)

+    {

+        assertEquals(localColumn, ref.getLocalColumn());

+        assertEquals(localColumn.getName(), ref.getLocalColumnName());

+        assertEquals(foreignColumn, ref.getForeignColumn());

+        assertEquals(foreignColumn.getName(), ref.getForeignColumnName());

+    }

+

+    /**

+     * Asserts data in an index object.

+     * 

+     * @param name       The expected name

+     * @param isUnique   The expected unique status

+     * @param numColumns The expected number of columns

+     * @param index      The index

+     */

+    private void assertEquals(String name, boolean isUnique, int numColumns, Index index)

+    {

+        assertEquals(name, index.getName());

+        assertEquals(isUnique, index.isUnique());

+        assertEquals(numColumns, index.getColumnCount());

+    }

+

+    /**

+     * Asserts data in an index column object.

+     * 

+     * @param column      The expected column

+     * @param size        The expected size value 

+     * @param indexColumn The index column

+     */

+    private void assertEquals(Column column, String size, IndexColumn indexColumn)

+    {

+        assertEquals(column, indexColumn.getColumn());

+        assertEquals(column.getName(), indexColumn.getName());

+        assertEquals(size, indexColumn.getSize());

+    }

+

+    /**

+     * Asserts that the given database model, written to XML, is equal to the given

+     * expected XML.

+     * 

+     * @param expectedXml The expected XML

+     * @param model       The database model

+     */

+    private void assertEquals(String expectedXml, Database model)

+    {

+        assertEquals(expectedXml, writeModel(model));

+    }

+

+    /**

+     * Tests an XML document without a database element.

+     */

+    public void testNoDatabaseElement()

+    {

+        assertNull(readModel("<data-base></data-base>"));

+    }

+

+    /**

+     * Tests that an exception is generated when the database element has no name attribute.

+     */

+    public void testDatabaseWithoutName()

+    {

+        try

+        {

+            readModel(

+                "<database>\n" +

+                "  <table name='TestTable'>\n" +

+                "    <column name='id'\n" +

+                "            type='INTEGER'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model without tables.

+     */

+    public void testNoTables() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "</database>");

+

+        assertEquals("test",

+                     model.getName());

+        assertEquals(0,

+                     model.getTableCount());

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\" />\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table without columns.

+     */

+    public void testTableWithoutColumns() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        assertEquals("SomeTable", "Some table", 0, 0, 0, 0, 0,

+                     model.getTable(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\" />\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests that an exception is generated when the table element has no name attribute.

+     */

+    public void testTableWithoutName()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table>\n" +

+                "    <column name='id'\n" +

+                "            type='INTEGER'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with a table with a single column.

+     */

+    public void testSingleColumn() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.INTEGER, 0, 0, null, null, null, false, false, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests that an exception is generated when the column element has no name attribute.

+     */

+    public void testColumnWithoutName()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TestTable'>\n" +

+                "    <column type='INTEGER'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests that an exception is generated when the column element has no type attribute.

+     */

+    public void testColumnWithoutType()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TestTable'>\n" +

+                "    <column name='id'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model columns of all possible datatypes.

+     */

+    public void testColumnTypes() throws Exception

+    {

+        StringBuffer modelXml = new StringBuffer();

+        int[]        types    = TypeMap.getSuportedJdbcTypes();

+

+        modelXml.append("<database name='test'>\n");

+        modelXml.append("  <table name='SomeTable'\n");

+        modelXml.append("         description='Some table'>\n");

+        for (int idx = 0; idx < types.length; idx++)

+        {

+            modelXml.append("    <column name='ID");

+            modelXml.append(idx);

+            modelXml.append("' type='");

+            modelXml.append(TypeMap.getJdbcTypeName(types[idx]));

+            modelXml.append("'/>\n");

+        }

+        modelXml.append("  </table>\n");

+        modelXml.append("</database>");

+

+        Database model = readModel(modelXml.toString());

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", types.length, 0, 0, 0, 0,

+                     table);

+

+        for (int idx = 0; idx < types.length; idx++)

+        {

+            assertEquals("ID" + idx, types[idx], 0, 0, null, null, null, false, false, false,

+                         table.getColumn(idx));

+        }

+

+        modelXml.setLength(0);

+        modelXml.append("<?xml version='1.0' encoding='UTF-8'?>\n");

+        modelXml.append("<database name=\"test\">\n");

+        modelXml.append("  <table name=\"SomeTable\" description=\"Some table\">\n");

+        for (int idx = 0; idx < types.length; idx++)

+        {

+            modelXml.append("    <column name=\"ID");

+            modelXml.append(idx);

+            modelXml.append("\" primaryKey=\"false\" required=\"false\" type=\"");

+            modelXml.append(TypeMap.getJdbcTypeName(types[idx]));

+            modelXml.append("\" autoIncrement=\"false\" />\n");

+        }

+        modelXml.append("  </table>\n");

+        modelXml.append("</database>\n");

+

+        assertEquals(modelXml.toString(), model);

+    }

+

+    /**

+     * Tests an illegal column type.

+     */

+    public void testColumnWithIllegalType()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TestTable'>\n" +

+                "    <column name='id'\n" +

+                "            type='illegal'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with a table with a primary key column.

+     */

+    public void testPrimaryKeyColumn() throws Exception

     {

         Database model = readModel(

             "<database name='test'>\n" +

@@ -88,61 +501,290 @@
             "         description='Some table'>\n" +

             "    <column name='ID'\n" +

             "            type='INTEGER'\n" +

-            "            primaryKey='true'\n" +

-            "            required='true'\n" +

-            "            description='The primary key'\n" +

-            "            javaName='javaId'/>\n" +

+            "            primaryKey='true'/>\n" +

             "  </table>\n" +

             "</database>");

 

-        assertEquals("test",

-                     model.getName());

-        assertEquals(1,

-                     model.getTableCount());

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

         

         Table table = model.getTable(0);

 

-        assertEquals("SomeTable",

-                     table.getName());

-        assertEquals("Some table",

-                     table.getDescription());

-        assertEquals(0, table.getAutoIncrementColumns().length);

-        assertEquals(1,

-                     table.getColumnCount());

-        assertEquals(0,

-                     table.getForeignKeyCount());

-        assertEquals(0,

-                     table.getIndexCount());

+        assertEquals("SomeTable", "Some table", 1, 1, 0, 0, 0,

+                     table);

 

         Column column = table.getColumn(0);

 

-        assertEquals("ID",

-                     column.getName());

-        assertEquals("INTEGER",

-                     column.getType());

-        assertEquals(Types.INTEGER,

-                     column.getTypeCode());

-        assertTrue(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The primary key",

-                     column.getDescription());

-        assertEquals("javaId", column.getJavaName());

+        assertEquals("ID", Types.INTEGER, 0, 0, null, null, null, true, false, false,

+                     column);

+

+        assertEquals(column, table.getPrimaryKeyColumns()[0]);

+        

         assertEquals(

-            "<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">\n" +

-            "  <database name=\"test\">\n" +

-            "    <table name=\"SomeTable\" description=\"Some table\">\n" +

-            "      <column name=\"ID\" primaryKey=\"true\" required=\"true\" type=\"INTEGER\" autoIncrement=\"false\" description=\"The primary key\" javaName=\"javaId\"/>\n" +

-            "    </table>\n" +

-            "  </database>\n",

-            writeModel(model));

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"true\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

     }

 

     /**

-     * Tests a database model containing a foreignkey.

+     * Tests a database model with a table with a required column.

      */

-    public void testForeignkey() throws Exception

+    public void testRequiredColumn() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='INTEGER'\n" +

+            "            required='true'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.INTEGER, 0, 0, null, null, null, false, true, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"true\" type=\"INTEGER\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table with an autoincrement column.

+     */

+    public void testAutoIncrementColumn() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='INTEGER'\n" +

+            "            autoIncrement='true'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 1, 0, 0,

+                     table);

+

+        Column column = table.getColumn(0);

+

+        assertEquals("ID", Types.INTEGER, 0, 0, null, null, null, false, false, true,

+                     column);

+

+        assertEquals(column, table.getAutoIncrementColumns()[0]);

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"true\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table with a column with a size spec.

+     */

+    public void testColumnWithSize1() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='20'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.VARCHAR, 20, 0, null, null, null, false, false, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"20\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table with a column with a size spec.

+     */

+    public void testColumnWithSize2() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='DECIMAL'\n" +

+            "            size='10,3'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.DECIMAL, 10, 3, null, null, null, false, false, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"DECIMAL\" size=\"10,3\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table with a column with a description.

+     */

+    public void testColumnWithDescription() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='INTEGER'\n" +

+            "            description='Foo'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.INTEGER, 0, 0, null, "Foo", null, false, false, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"Foo\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table with a column with a default.

+     */

+    public void testColumnWithDefault() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='32'\n" +

+            "            default='Test string'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.VARCHAR, 32, 0, "Test string", null, null, false, false, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"32\" default=\"Test string\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with a table with a column with a java name.

+     */

+    public void testColumnWithJavaName() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='DOUBLE'\n" +

+            "            javaName='testString'/>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 0, 0, 0, 0,

+                     table);

+        assertEquals("ID", Types.DOUBLE, 0, 0, null, null, "testString", false, false, false,

+                     table.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"false\" required=\"false\" type=\"DOUBLE\" autoIncrement=\"false\" javaName=\"testString\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model containing a single foreignkey.

+     */

+    public void testSingleForeignkey() throws Exception

     {

         Database model = readModel(

             "<database name='test'>\n" +

@@ -167,117 +809,612 @@
             "  </table>\n" +

             "</database>");

 

-        assertEquals("test",

-                     model.getName());

-        assertEquals(2,

-                     model.getTableCount());

+        assertEquals("test", model.getName());

+        assertEquals(2, model.getTableCount());

 

         Table someTable = model.getTable(0);

 

-        assertEquals("SomeTable",

-                     someTable.getName());

-        assertEquals("Some table",

-                     someTable.getDescription());

-        assertEquals(0, someTable.getAutoIncrementColumns().length);

-        assertEquals(1,

-                     someTable.getColumnCount());

-        assertEquals(0,

-                     someTable.getForeignKeyCount());

-        assertEquals(0,

-                     someTable.getIndexCount());

+        assertEquals("SomeTable", "Some table", 1, 1, 0, 0, 0,

+                     someTable);

 

         Column pkColumn = someTable.getColumn(0);

 

-        assertEquals("ID",

-                     pkColumn.getName());

-        assertEquals("VARCHAR",

-                     pkColumn.getType());

-        assertEquals(Types.VARCHAR,

-                     pkColumn.getTypeCode());

-        assertEquals(16,

-                     pkColumn.getSizeAsInt());

-        assertTrue(pkColumn.isPrimaryKey());

-        assertTrue(pkColumn.isRequired());

-        assertFalse(pkColumn.isAutoIncrement());

-        assertNull(pkColumn.getDefaultValue());

-        assertEquals("The primary key",

-                     pkColumn.getDescription());

+        assertEquals("ID", Types.VARCHAR, 16, 0, null, "The primary key", null, true, true, false,

+                     pkColumn);

 

         Table anotherTable = model.getTable(1);

 

-        assertEquals("AnotherTable",

-                     anotherTable.getName());

-        assertEquals("And another table",

-                     anotherTable.getDescription());

-        assertEquals(0, anotherTable.getAutoIncrementColumns().length);

-        assertEquals(1,

-                     anotherTable.getColumnCount());

-        assertEquals(1,

-                     anotherTable.getForeignKeyCount());

-        assertEquals(0,

-                     anotherTable.getIndexCount());

+        assertEquals("AnotherTable", "And another table", 1, 0, 0, 1, 0,

+                     anotherTable);

 

         Column fkColumn = anotherTable.getColumn(0);

 

-        assertEquals("Some_ID",

-                     fkColumn.getName());

-        assertEquals("VARCHAR",

-                     fkColumn.getType());

-        assertEquals(Types.VARCHAR,

-                     fkColumn.getTypeCode());

-        assertEquals(16,

-                     fkColumn.getSizeAsInt());

-        assertFalse(fkColumn.isPrimaryKey());

-        assertFalse(fkColumn.isRequired());

-        assertFalse(fkColumn.isAutoIncrement());

-        assertEquals("The foreign key",

-                     fkColumn.getDescription());

+        assertEquals("Some_ID", Types.VARCHAR, 16, 0, null, "The foreign key", null, false, false, false,

+                     fkColumn);

 

         ForeignKey fk = anotherTable.getForeignKey(0);

 

-        assertNull(fk.getName());

-        assertEquals(someTable,

-                     fk.getForeignTable());

-        assertEquals(someTable.getName(),

-                     fk.getForeignTableName());

-        assertEquals(1,

-                     fk.getReferenceCount());

-

-        Reference ref = fk.getFirstReference();

-

-        assertEquals(fkColumn,

-                     ref.getLocalColumn());

-        assertEquals("Some_ID",

-                     ref.getLocalColumnName());

-        assertEquals(pkColumn,

-                     ref.getForeignColumn());

-        assertEquals("ID",

-                     ref.getForeignColumnName());

+        assertEquals(null, CascadeActionEnum.NONE, CascadeActionEnum.NONE, someTable, 1, fk);

+        assertEquals(fkColumn, pkColumn, fk.getFirstReference());

 

         assertEquals(

-            "<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">\n" +

-            "  <database name=\"test\">\n" +

-            "    <table name=\"SomeTable\" description=\"Some table\">\n" +

-            "      <column name=\"ID\" primaryKey=\"true\" required=\"true\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The primary key\"/>\n" +

-            "    </table>\n" +

-            "    <table name=\"AnotherTable\" description=\"And another table\">\n" +

-            "      <column name=\"Some_ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The foreign key\"/>\n" +

-            "      <foreign-key foreignTable=\"SomeTable\">\n" +

-            "        <reference local=\"Some_ID\" foreign=\"ID\"/>\n" +

-            "      </foreign-key>\n" +

-            "    </table>\n" +

-            "  </database>\n",

-            writeModel(model));

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"true\" required=\"true\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The primary key\" />\n" +

+            "  </table>\n" +

+            "  <table name=\"AnotherTable\" description=\"And another table\">\n" +

+            "    <column name=\"Some_ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The foreign key\" />\n" +

+            "    <foreign-key foreignTable=\"SomeTable\">\n" +

+            "      <reference local=\"Some_ID\" foreign=\"ID\" />\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

     }

 

     /**

-     * Tests a database model with indices.

+     * Tests a database model containing a foreignkey with two references.

      */

-    public void testIndices1() throws Exception

+    public void testForeignkeyWithTwoReferences() throws Exception

     {

         Database model = readModel(

             "<database name='test'>\n" +

-            "  <table name='TableWidthIndex'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='16'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'\n" +

+            "            description='The primary key'/>\n" +

+            "    <column name='VALUE1'\n" +

+            "            type='INTEGER'\n" +

+            "            required='false'\n" +

+            "            description='A value'/>\n" +

+            "    <column name='VALUE2'\n" +

+            "            type='DOUBLE'\n" +

+            "            required='false'\n" +

+            "            description='Another value'/>\n" +

+            "  </table>\n" +

+            "  <table name='AnotherTable'\n" +

+            "         description='And another table'>\n" +

+            "    <column name='Some_ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='16'\n" +

+            "            description='The foreign key'/>\n" +

+            "    <column name='Some_Value'\n" +

+            "            type='DOUBLE'/>\n" +

+            "    <foreign-key foreignTable='SomeTable'>\n" +

+            "       <reference local='Some_ID' foreign='ID'/>\n" +

+            "       <reference local='Some_Value' foreign='VALUE2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(2, model.getTableCount());

+

+        Table someTable = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 3, 1, 0, 0, 0,

+                     someTable);

+        assertEquals("ID", Types.VARCHAR, 16, 0, null, "The primary key", null, true, true, false,

+                     someTable.getColumn(0));

+

+        Table anotherTable = model.getTable(1);

+

+        assertEquals("AnotherTable", "And another table", 2, 0, 0, 1, 0,

+                     anotherTable);

+        assertEquals("Some_ID", Types.VARCHAR, 16, 0, null, "The foreign key", null, false, false, false,

+                     anotherTable.getColumn(0));

+

+        ForeignKey fk = anotherTable.getForeignKey(0);

+

+        assertEquals(null, CascadeActionEnum.NONE, CascadeActionEnum.NONE, someTable, 2, fk);

+        assertEquals(anotherTable.getColumn(0), someTable.getColumn(0), fk.getReference(0));

+        assertEquals(anotherTable.getColumn(1), someTable.getColumn(2), fk.getReference(1));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"true\" required=\"true\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The primary key\" />\n" +

+            "    <column name=\"VALUE1\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"A value\" />\n" +

+            "    <column name=\"VALUE2\" primaryKey=\"false\" required=\"false\" type=\"DOUBLE\" autoIncrement=\"false\" description=\"Another value\" />\n" +

+            "  </table>\n" +

+            "  <table name=\"AnotherTable\" description=\"And another table\">\n" +

+            "    <column name=\"Some_ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The foreign key\" />\n" +

+            "    <column name=\"Some_Value\" primaryKey=\"false\" required=\"false\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <foreign-key foreignTable=\"SomeTable\">\n" +

+            "      <reference local=\"Some_ID\" foreign=\"ID\" />\n" +

+            "      <reference local=\"Some_Value\" foreign=\"VALUE2\" />\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a foreign key without references.

+     */

+    public void testForeignKeyWithoutReferences()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <foreign-key foreignTable='SomeTable'>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model containing a named foreignkey.

+     */

+    public void testNamedForeignkey() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='16'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'\n" +

+            "            description='The primary key'/>\n" +

+            "  </table>\n" +

+            "  <table name='AnotherTable'\n" +

+            "         description='And another table'>\n" +

+            "    <column name='Some_ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='16'\n" +

+            "            description='The foreign key'/>\n" +

+            "    <foreign-key name='The foreignkey' foreignTable='SomeTable'>\n" +

+            "       <reference local='Some_ID' foreign='ID'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(2, model.getTableCount());

+

+        Table someTable = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 1, 1, 0, 0, 0,

+                     someTable);

+        assertEquals("ID", Types.VARCHAR, 16, 0, null, "The primary key", null, true, true, false,

+                     someTable.getColumn(0));

+

+        Table anotherTable = model.getTable(1);

+

+        assertEquals("AnotherTable", "And another table", 1, 0, 0, 1, 0,

+                     anotherTable);

+        assertEquals("Some_ID", Types.VARCHAR, 16, 0, null, "The foreign key", null, false, false, false,

+                     anotherTable.getColumn(0));

+

+        ForeignKey fk = anotherTable.getForeignKey(0);

+

+        assertEquals("The foreignkey", CascadeActionEnum.NONE, CascadeActionEnum.NONE, someTable, 1, fk);

+        assertEquals(anotherTable.getColumn(0), someTable.getColumn(0), fk.getReference(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"true\" required=\"true\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The primary key\" />\n" +

+            "  </table>\n" +

+            "  <table name=\"AnotherTable\" description=\"And another table\">\n" +

+            "    <column name=\"Some_ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The foreign key\" />\n" +

+            "    <foreign-key foreignTable=\"SomeTable\" name=\"The foreignkey\">\n" +

+            "      <reference local=\"Some_ID\" foreign=\"ID\" />\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a foreign key referencing a non-existing table.

+     */

+    public void testForeignKeyReferencingUndefinedTable()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <foreign-key foreignTable='TheTable'>\n" +

+                "       <reference local='Some_ID' foreign='ID'/>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a foreign key using a non-existing column in the local table.

+     */

+    public void testForeignKeyUsingUndefinedColumn()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <foreign-key foreignTable='SomeTable'>\n" +

+                "       <reference local='ID' foreign='ID'/>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a foreign key referencing a non-existing column in the foreign table.

+     */

+    public void testForeignKeyReferencingUndefinedColumn()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <foreign-key foreignTable='SomeTable'>\n" +

+                "       <reference local='Some_ID' foreign='TheID'/>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a foreign key without a local column.

+     */

+    public void testForeignKeyWithoutLocalColumn()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <foreign-key foreignTable='SomeTable'>\n" +

+                "       <reference foreign='ID'/>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a foreign key without a remote column.

+     */

+    public void testForeignKeyWithoutRemoteColumn()

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <foreign-key foreignTable='SomeTable'>\n" +

+                "       <reference local='Some_ID'/>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model containing two foreignkeys.

+     */

+    public void testTwoForeignkeys() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='SomeTable'\n" +

+            "         description='Some table'>\n" +

+            "    <column name='ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='16'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'\n" +

+            "            description='The primary key'/>\n" +

+            "    <column name='VALUE1'\n" +

+            "            type='INTEGER'\n" +

+            "            required='false'\n" +

+            "            description='A value'/>\n" +

+            "    <column name='VALUE2'\n" +

+            "            type='DOUBLE'\n" +

+            "            required='false'\n" +

+            "            description='Another value'/>\n" +

+            "  </table>\n" +

+            "  <table name='AnotherTable'\n" +

+            "         description='And another table'>\n" +

+            "    <column name='Some_ID'\n" +

+            "            type='VARCHAR'\n" +

+            "            size='16'\n" +

+            "            description='The foreign key'/>\n" +

+            "    <column name='Some_Value'\n" +

+            "            type='DOUBLE'/>\n" +

+            "    <foreign-key foreignTable='SomeTable'>\n" +

+            "       <reference local='Some_ID' foreign='ID'/>\n" +

+            "    </foreign-key>\n" +

+            "    <foreign-key foreignTable='SomeTable'>\n" +

+            "       <reference local='Some_Value' foreign='VALUE2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(2, model.getTableCount());

+

+        Table someTable = model.getTable(0);

+

+        assertEquals("SomeTable", "Some table", 3, 1, 0, 0, 0,

+                     someTable);

+        assertEquals("ID", Types.VARCHAR, 16, 0, null, "The primary key", null, true, true, false,

+                     someTable.getColumn(0));

+

+        Table anotherTable = model.getTable(1);

+

+        assertEquals("AnotherTable", "And another table", 2, 0, 0, 2, 0,

+                     anotherTable);

+        assertEquals("Some_ID", Types.VARCHAR, 16, 0, null, "The foreign key", null, false, false, false,

+                     anotherTable.getColumn(0));

+

+        ForeignKey fk = anotherTable.getForeignKey(0);

+

+        assertEquals(null, CascadeActionEnum.NONE, CascadeActionEnum.NONE, someTable, 1, fk);

+        assertEquals(anotherTable.getColumn(0), someTable.getColumn(0), fk.getReference(0));

+

+        fk = anotherTable.getForeignKey(1);

+

+        assertEquals(null, CascadeActionEnum.NONE, CascadeActionEnum.NONE, someTable, 1, fk);

+        assertEquals(anotherTable.getColumn(1), someTable.getColumn(2), fk.getReference(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\" description=\"Some table\">\n" +

+            "    <column name=\"ID\" primaryKey=\"true\" required=\"true\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The primary key\" />\n" +

+            "    <column name=\"VALUE1\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"A value\" />\n" +

+            "    <column name=\"VALUE2\" primaryKey=\"false\" required=\"false\" type=\"DOUBLE\" autoIncrement=\"false\" description=\"Another value\" />\n" +

+            "  </table>\n" +

+            "  <table name=\"AnotherTable\" description=\"And another table\">\n" +

+            "    <column name=\"Some_ID\" primaryKey=\"false\" required=\"false\" type=\"VARCHAR\" size=\"16\" autoIncrement=\"false\" description=\"The foreign key\" />\n" +

+            "    <column name=\"Some_Value\" primaryKey=\"false\" required=\"false\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <foreign-key foreignTable=\"SomeTable\">\n" +

+            "      <reference local=\"Some_ID\" foreign=\"ID\" />\n" +

+            "    </foreign-key>\n" +

+            "    <foreign-key foreignTable=\"SomeTable\">\n" +

+            "      <reference local=\"Some_Value\" foreign=\"VALUE2\" />\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model containing two foreignkeys with the same name.

+     */

+    public void testTwoForeignkeysWithSameName() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='SomeTable'\n" +

+                "         description='Some table'>\n" +

+                "    <column name='ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'\n" +

+                "            description='The primary key'/>\n" +

+                "    <column name='VALUE1'\n" +

+                "            type='INTEGER'\n" +

+                "            required='false'\n" +

+                "            description='A value'/>\n" +

+                "    <column name='VALUE2'\n" +

+                "            type='DOUBLE'\n" +

+                "            required='false'\n" +

+                "            description='Another value'/>\n" +

+                "  </table>\n" +

+                "  <table name='AnotherTable'\n" +

+                "         description='And another table'>\n" +

+                "    <column name='Some_ID'\n" +

+                "            type='VARCHAR'\n" +

+                "            size='16'\n" +

+                "            description='The foreign key'/>\n" +

+                "    <column name='Some_Value'\n" +

+                "            type='DOUBLE'/>\n" +

+                "    <foreign-key name='The foreignkey' foreignTable='SomeTable'>\n" +

+                "       <reference local='Some_ID' foreign='ID'/>\n" +

+                "    </foreign-key>\n" +

+                "    <foreign-key name='The foreignkey' foreignTable='SomeTable'>\n" +

+                "       <reference local='Some_Value' foreign='VALUE2'/>\n" +

+                "    </foreign-key>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with an index.

+     */

+    public void testSingleIndex() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='TableWithIndex'>\n" +

+            "    <column name='id'\n" +

+            "            type='DOUBLE'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'/>\n" +

+            "    <column name='value'\n" +

+            "            type='SMALLINT'\n" +

+            "            default='1'/>\n" +

+            "    <index>\n" +

+            "      <index-column name='value'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("TableWithIndex", null, 2, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.DOUBLE, 0, 0, null, null, null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("value", Types.SMALLINT, 0, 0, "1", null, null, false, false, false,

+                     table.getColumn(1));

+

+        Index index = table.getIndex(0);

+

+        assertEquals(null, false, 1, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndex\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\" />\n" +

+            "    <index>\n" +

+            "      <index-column name=\"value\" />\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with an index with two columns.

+     */

+    public void testIndexWithTwoColumns() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='TableWithIndex'>\n" +

             "    <column name='id'\n" +

             "            type='DOUBLE'\n" +

             "            primaryKey='true'\n" +

@@ -288,9 +1425,6 @@
             "    <column name='value'\n" +

             "            type='SMALLINT'\n" +

             "            default='1'/>\n" +

-            "    <index name='test index'>\n" +

-            "      <index-column name='value'/>\n" +

-            "    </index>\n" +

             "    <index>\n" +

             "      <index-column name='when'/>\n" +

             "      <index-column name='id'/>\n" +

@@ -298,128 +1432,440 @@
             "  </table>\n" +

             "</database>");

 

-        assertEquals("test",

-                     model.getName());

-        assertEquals(1,

-                     model.getTableCount());

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

         

         Table table = model.getTable(0);

 

-        assertEquals("TableWidthIndex",

-                     table.getName());

-        assertNull(table.getDescription());

-        assertEquals(0, table.getAutoIncrementColumns().length);

-        assertEquals(3,

-                     table.getColumnCount());

-        assertEquals(0,

-                     table.getForeignKeyCount());

-        assertEquals(2,

-                     table.getIndexCount());

-

-        Column column = table.getColumn(0);

-

-        assertEquals("id",

-                     column.getName());

-        assertEquals("DOUBLE",

-                     column.getType());

-        assertEquals(Types.DOUBLE,

-                     column.getTypeCode());

-        assertTrue(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDescription());

-

-        column = table.getColumn(1);

-

-        assertEquals("when",

-                     column.getName());

-        assertEquals("TIMESTAMP",

-                     column.getType());

-        assertEquals(Types.TIMESTAMP,

-                     column.getTypeCode());

-        assertFalse(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDescription());

-

-        column = table.getColumn(2);

-

-        assertEquals("value",

-                     column.getName());

-        assertEquals("SMALLINT",

-                     column.getType());

-        assertEquals(Types.SMALLINT,

-                     column.getTypeCode());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertEquals("1",

-                     column.getDefaultValue());

-        assertNull(column.getDescription());

+        assertEquals("TableWithIndex", null, 3, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.DOUBLE, 0, 0, null, null, null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("when", Types.TIMESTAMP, 0, 0, null, null, null, false, true, false,

+                     table.getColumn(1));

+        assertEquals("value", Types.SMALLINT, 0, 0, "1", null, null, false, false, false,

+                     table.getColumn(2));

 

         Index index = table.getIndex(0);

 

-        assertEquals("test index",

-                     index.getName());

-        assertFalse(index.isUnique());

-        assertEquals(1,

-                     index.getColumnCount());

-

-        IndexColumn indexColumn = index.getColumn(0);

-

-        assertEquals("value",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

-

-        index = table.getIndex(1);

-

-        assertNull(index.getName());

-        assertFalse(index.isUnique());

-        assertEquals(2,

-                     index.getColumnCount());

-

-        indexColumn = index.getColumn(0);

-

-        assertEquals("when",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

-

-        indexColumn = index.getColumn(1);

-

-        assertEquals("id",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

+        assertEquals(null, false, 2, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+        assertEquals(table.getColumn(0), null, index.getColumn(1));

 

         assertEquals(

-            "<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">\n" +

-            "  <database name=\"test\">\n" +

-            "    <table name=\"TableWidthIndex\">\n" +

-            "      <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\"/>\n" +

-            "      <column name=\"when\" primaryKey=\"false\" required=\"true\" type=\"TIMESTAMP\" autoIncrement=\"false\"/>\n" +

-            "      <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\"/>\n" +

-            "      <index name=\"test index\">\n" +

-            "        <index-column name=\"value\"/>\n" +

-            "      </index>\n" +

-            "      <index>\n" +

-            "        <index-column name=\"when\"/>\n" +

-            "        <index-column name=\"id\"/>\n" +

-            "      </index>\n" +

-            "    </table>\n" +

-            "  </database>\n",

-            writeModel(model));

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndex\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"when\" primaryKey=\"false\" required=\"true\" type=\"TIMESTAMP\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\" />\n" +

+            "    <index>\n" +

+            "      <index-column name=\"when\" />\n" +

+            "      <index-column name=\"id\" />\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with an index with a name.

+     */

+    public void testIndexWithName() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='TableWithIndex'>\n" +

+            "    <column name='id'\n" +

+            "            type='DOUBLE'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'/>\n" +

+            "    <column name='value'\n" +

+            "            type='SMALLINT'\n" +

+            "            default='1'/>\n" +

+            "    <index name='The Index'>\n" +

+            "      <index-column name='value'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("TableWithIndex", null, 2, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.DOUBLE, 0, 0, null, null, null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("value", Types.SMALLINT, 0, 0, "1", null, null, false, false, false,

+                     table.getColumn(1));

+

+        Index index = table.getIndex(0);

+

+        assertEquals("The Index", false, 1, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndex\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\" />\n" +

+            "    <index name=\"The Index\">\n" +

+            "      <index-column name=\"value\" />\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with an index without index columns.

+     */

+    public void testIndexWithoutColumns() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TableWithIndex'>\n" +

+                "    <column name='id'\n" +

+                "            type='DOUBLE'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "    <column name='value'\n" +

+                "            type='SMALLINT'\n" +

+                "            default='1'/>\n" +

+                "    <index>\n" +

+                "    </index>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with an index with an index column that references an undefined column.

+     */

+    public void testIndexWithUndefinedColumns() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TableWithIndex'>\n" +

+                "    <column name='id'\n" +

+                "            type='DOUBLE'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "    <column name='value'\n" +

+                "            type='SMALLINT'\n" +

+                "            default='1'/>\n" +

+                "    <index>\n" +

+                "      <index-column name='theValue'/>\n" +

+                "    </index>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with an index with an index column that has no name.

+     */

+    public void testIndexWithNoNameColumn() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TableWithIndex'>\n" +

+                "    <column name='id'\n" +

+                "            type='DOUBLE'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "    <column name='value'\n" +

+                "            type='SMALLINT'\n" +

+                "            default='1'/>\n" +

+                "    <index>\n" +

+                "      <index-column/>\n" +

+                "    </index>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+

+    /**

+     * Tests a database model with an unique index.

+     */

+    public void testSingleUniqueIndex() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='TableWithIndex'>\n" +

+            "    <column name='id'\n" +

+            "            type='DOUBLE'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'/>\n" +

+            "    <column name='value'\n" +

+            "            type='SMALLINT'\n" +

+            "            default='1'/>\n" +

+            "    <unique>\n" +

+            "      <unique-column name='value'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("TableWithIndex", null, 2, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.DOUBLE, 0, 0, null, null, null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("value", Types.SMALLINT, 0, 0, "1", null, null, false, false, false,

+                     table.getColumn(1));

+

+        Index index = table.getIndex(0);

+

+        assertEquals(null, true, 1, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndex\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\" />\n" +

+            "    <unique>\n" +

+            "      <unique-column name=\"value\" />\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with an unique index with two columns.

+     */

+    public void testUniqueIndexWithTwoColumns() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='TableWithIndex'>\n" +

+            "    <column name='id'\n" +

+            "            type='DOUBLE'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'/>\n" +

+            "    <column name='when'\n" +

+            "            type='TIMESTAMP'\n" +

+            "            required='true'/>\n" +

+            "    <column name='value'\n" +

+            "            type='SMALLINT'\n" +

+            "            default='1'/>\n" +

+            "    <unique>\n" +

+            "      <unique-column name='when'/>\n" +

+            "      <unique-column name='id'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("TableWithIndex", null, 3, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.DOUBLE, 0, 0, null, null, null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("when", Types.TIMESTAMP, 0, 0, null, null, null, false, true, false,

+                     table.getColumn(1));

+        assertEquals("value", Types.SMALLINT, 0, 0, "1", null, null, false, false, false,

+                     table.getColumn(2));

+

+        Index index = table.getIndex(0);

+

+        assertEquals(null, true, 2, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+        assertEquals(table.getColumn(0), null, index.getColumn(1));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndex\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"when\" primaryKey=\"false\" required=\"true\" type=\"TIMESTAMP\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\" />\n" +

+            "    <unique>\n" +

+            "      <unique-column name=\"when\" />\n" +

+            "      <unique-column name=\"id\" />\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with an unique index with a name.

+     */

+    public void testUniqueIndexWithName() throws Exception

+    {

+        Database model = readModel(

+            "<database name='test'>\n" +

+            "  <table name='TableWithIndex'>\n" +

+            "    <column name='id'\n" +

+            "            type='DOUBLE'\n" +

+            "            primaryKey='true'\n" +

+            "            required='true'/>\n" +

+            "    <column name='value'\n" +

+            "            type='SMALLINT'\n" +

+            "            default='1'/>\n" +

+            "    <unique name='The Index'>\n" +

+            "      <unique-column name='value'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>");

+

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

+        

+        Table table = model.getTable(0);

+

+        assertEquals("TableWithIndex", null, 2, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.DOUBLE, 0, 0, null, null, null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("value", Types.SMALLINT, 0, 0, "1", null, null, false, false, false,

+                     table.getColumn(1));

+

+        Index index = table.getIndex(0);

+

+        assertEquals("The Index", true, 1, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+

+        assertEquals(

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndex\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"DOUBLE\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"value\" primaryKey=\"false\" required=\"false\" type=\"SMALLINT\" default=\"1\" autoIncrement=\"false\" />\n" +

+            "    <unique name=\"The Index\">\n" +

+            "      <unique-column name=\"value\" />\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

+    }

+

+    /**

+     * Tests a database model with an unique index without index columns.

+     */

+    public void testUniqueIndexWithoutColumns() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TableWithIndex'>\n" +

+                "    <column name='id'\n" +

+                "            type='DOUBLE'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "    <column name='value'\n" +

+                "            type='SMALLINT'\n" +

+                "            default='1'/>\n" +

+                "    <unique>\n" +

+                "    </unique>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with an unique index with an index column that references an undefined column.

+     */

+    public void testUniqueIndexWithUndefinedColumns() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TableWithIndex'>\n" +

+                "    <column name='id'\n" +

+                "            type='DOUBLE'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "    <column name='value'\n" +

+                "            type='SMALLINT'\n" +

+                "            default='1'/>\n" +

+                "    <unique>\n" +

+                "      <unique-column name='theValue'/>\n" +

+                "    </unique>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

+    }

+

+    /**

+     * Tests a database model with an unique index with an index column that has no name.

+     */

+    public void testUniqueIndexWithNoNameColumn() throws Exception

+    {

+        try

+        {

+            readModel(

+                "<database name='test'>\n" +

+                "  <table name='TableWithIndex'>\n" +

+                "    <column name='id'\n" +

+                "            type='DOUBLE'\n" +

+                "            primaryKey='true'\n" +

+                "            required='true'/>\n" +

+                "    <column name='value'\n" +

+                "            type='SMALLINT'\n" +

+                "            default='1'/>\n" +

+                "    <unique>\n" +

+                "      <unique-column/>\n" +

+                "    </unique>\n" +

+                "  </table>\n" +

+                "</database>");

+

+            fail();

+        }

+        catch (ModelException ex)

+        {}

     }

 

     /**

      * Tests a database model with indices, both uniques and non-uniques.

      */

-    public void testIndices2() throws Exception

+    public void testMixedIndexes() throws Exception

     {

         Database model = readModel(

             "<database name='test'>\n" +

-            "  <table name='TableWidthIndices'>\n" +

+            "  <table name='TableWithIndexes'>\n" +

             "    <column name='id'\n" +

             "            type='SMALLINT'\n" +

             "            primaryKey='false'\n" +

@@ -436,97 +1882,44 @@
             "  </table>\n" +

             "</database>");

 

-        assertEquals("test",

-                     model.getName());

-        assertEquals(1,

-                     model.getTableCount());

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

         

         Table table = model.getTable(0);

 

-        assertEquals("TableWidthIndices",

-                     table.getName());

-        assertNull(table.getDescription());

-        assertEquals(2,

-                     table.getColumnCount());

-        assertEquals(0,

-                     table.getForeignKeyCount());

-        assertEquals(2,

-                     table.getIndexCount());

-

-        Column column = table.getColumn(0);

-

-        assertEquals("id",

-                     column.getName());

-        assertEquals("SMALLINT",

-                     column.getType());

-        assertEquals(Types.SMALLINT,

-                     column.getTypeCode());

-        assertFalse(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertTrue(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDescription());

-        

-        assertEquals(1, table.getAutoIncrementColumns().length);

-        assertEquals(column, table.getAutoIncrementColumns()[0]);

-

-        column = table.getColumn(1);

-

-        assertEquals("when",

-                     column.getName());

-        assertEquals("DATE",

-                     column.getType());

-        assertEquals(Types.DATE,

-                     column.getTypeCode());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDescription());

+        assertEquals("TableWithIndexes", null, 2, 0, 1, 0, 2,

+                     table);

+        assertEquals("id", Types.SMALLINT, 0, 0, null, null, null, false, true, true,

+                     table.getColumn(0));

+        assertEquals("when", Types.DATE, 0, 0, null, null, null, false, false, false,

+                     table.getColumn(1));

+        assertEquals(table.getColumn(0), table.getAutoIncrementColumns()[0]);

 

         Index index = table.getIndex(0);

 

-        assertEquals("important column",

-                     index.getName());

-        assertTrue(index.isUnique());

-        assertEquals(1,

-                     index.getColumnCount());

-

-        IndexColumn indexColumn = index.getColumn(0);

-

-        assertEquals("id",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

+        assertEquals("important column", true, 1, index);

+        assertEquals(table.getColumn(0), null, index.getColumn(0));

 

         index = table.getIndex(1);

 

-        assertNull(index.getName());

-        assertFalse(index.isUnique());

-        assertEquals(1,

-                     index.getColumnCount());

-

-        indexColumn = index.getColumn(0);

-

-        assertEquals("when",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

+        assertEquals(null, false, 1, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

 

         assertEquals(

-            "<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">\n" +

-            "  <database name=\"test\">\n" +

-            "    <table name=\"TableWidthIndices\">\n" +

-            "      <column name=\"id\" primaryKey=\"false\" required=\"true\" type=\"SMALLINT\" autoIncrement=\"true\"/>\n" +

-            "      <column name=\"when\" primaryKey=\"false\" required=\"false\" type=\"DATE\" autoIncrement=\"false\"/>\n" +

-            "      <unique name=\"important column\">\n" +

-            "        <unique-column name=\"id\"/>\n" +

-            "      </unique>\n" +

-            "      <index>\n" +

-            "        <index-column name=\"when\"/>\n" +

-            "      </index>\n" +

-            "    </table>\n" +

-            "  </database>\n",

-            writeModel(model));

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"TableWithIndexes\">\n" +

+            "    <column name=\"id\" primaryKey=\"false\" required=\"true\" type=\"SMALLINT\" autoIncrement=\"true\" />\n" +

+            "    <column name=\"when\" primaryKey=\"false\" required=\"false\" type=\"DATE\" autoIncrement=\"false\" />\n" +

+            "    <unique name=\"important column\">\n" +

+            "      <unique-column name=\"id\" />\n" +

+            "    </unique>\n" +

+            "    <index>\n" +

+            "      <index-column name=\"when\" />\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

     }

 

     /**

@@ -605,356 +1998,116 @@
             "  </table>\n" +

             "</database>");

 

-        assertEquals("test",

-                     model.getName());

-        assertEquals(3,

-                     model.getTableCount());

+        assertEquals("test", model.getName());

+        assertEquals(3, model.getTableCount());

 

         // table A

 

         Table table = model.getTable(0);

 

-        assertEquals("A",

-                     table.getName());

-        assertEquals("Table A",

-                     table.getDescription());

-        assertEquals(3,

-                     table.getColumnCount());

-        assertEquals(1,

-                     table.getForeignKeyCount());

-        assertEquals(1,

-                     table.getIndexCount());

-

-        Column column = table.getColumn(0);

-

-        assertEquals("id",

-                     column.getName());

-        assertEquals("INTEGER",

-                     column.getType());

-        assertEquals(Types.INTEGER,

-                     column.getTypeCode());

-        assertNull(column.getSize());

-        assertEquals(0,

-                     column.getSizeAsInt());

-        assertTrue(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertTrue(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The primary key of table A",

-                     column.getDescription());

-        assertEquals(1, table.getAutoIncrementColumns().length);

-        assertEquals(column,

-                     table.getAutoIncrementColumns()[0]);

-

-        column = table.getColumn(1);

-

-        assertEquals("parentId",

-                     column.getName());

-        assertEquals("INTEGER",

-                     column.getType());

-        assertEquals(Types.INTEGER,

-                     column.getTypeCode());

-        assertNull(column.getSize());

-        assertEquals(0,

-                     column.getSizeAsInt());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The field for the foreign key parent",

-                     column.getDescription());

-

-        column = table.getColumn(2);

-

-        assertEquals("name",

-                     column.getName());

-        assertEquals("VARCHAR",

-                     column.getType());

-        assertEquals(Types.VARCHAR,

-                     column.getTypeCode());

-        assertEquals("32",

-                     column.getSize());

-        assertEquals(32,

-                     column.getSizeAsInt());

-        assertFalse(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The name",

-                     column.getDescription());

+        assertEquals("A", "Table A", 3, 1, 1, 1, 1,

+                     table);

+        assertEquals("id", Types.INTEGER, 0, 0, null, "The primary key of table A", null, true, true, true,

+                     table.getColumn(0));

+        assertEquals("parentId", Types.INTEGER, 0, 0, null, "The field for the foreign key parent", null, false, false, false,

+                     table.getColumn(1));

+        assertEquals("name", Types.VARCHAR, 32, 0, null, "The name", null, false, true, false,

+                     table.getColumn(2));

+        assertEquals(table.getColumn(0), table.getAutoIncrementColumns()[0]);

 

         ForeignKey fk = table.getForeignKey(0);

 

-        assertEquals("parent",

-                     fk.getName());

-        assertEquals(table,

-                     fk.getForeignTable());

-        assertEquals("A",

-                     fk.getForeignTableName());

-        assertEquals(1,

-                     fk.getReferenceCount());

-

-        Reference ref = fk.getFirstReference();

-

-        assertEquals(table.getColumn(1),

-                     ref.getLocalColumn());

-        assertEquals("parentId",

-                     ref.getLocalColumnName());

-        assertEquals(table.getColumn(0),

-                     ref.getForeignColumn());

-        assertEquals("id",

-                     ref.getForeignColumnName());

+        assertEquals("parent", CascadeActionEnum.NONE, CascadeActionEnum.NONE, table, 1, fk);

+        assertEquals(table.getColumn(1), table.getColumn(0), fk.getFirstReference());

 

         Index index = table.getIndex(0);

 

-        assertNull(index.getName());

-        assertTrue(index.isUnique());

-        assertEquals(1,

-                     index.getColumnCount());

-

-        IndexColumn indexColumn = index.getColumn(0);

-

-        assertEquals("name",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

+        assertEquals(null, true, 1, index);

+        assertEquals(table.getColumn(2), null, index.getColumn(0));

 

         // table B

         

         table = model.getTable(1);

 

-        assertEquals("B",

-                     table.getName());

-        assertEquals("Table B",

-                     table.getDescription());

-        assertEquals(0, table.getAutoIncrementColumns().length);

-        assertEquals(3,

-                     table.getColumnCount());

-        assertEquals(2,

-                     table.getForeignKeyCount());

-        assertEquals(1,

-                     table.getIndexCount());

-

-        column = table.getColumn(0);

-

-        assertEquals("id",

-                     column.getName());

-        assertEquals("TIMESTAMP",

-                     column.getType());

-        assertEquals(Types.TIMESTAMP,

-                     column.getTypeCode());

-        assertNull(column.getSize());

-        assertEquals(0,

-                     column.getSizeAsInt());

-        assertTrue(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The primary key of table B",

-                     column.getDescription());

-

-        column = table.getColumn(1);

-

-        assertEquals("aid",

-                     column.getName());

-        assertEquals("INTEGER",

-                     column.getType());

-        assertEquals(Types.INTEGER,

-                     column.getTypeCode());

-        assertNull(column.getSize());

-        assertEquals(0,

-                     column.getSizeAsInt());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The field for the foreign key towards A",

-                     column.getDescription());

-

-        column = table.getColumn(2);

-

-        assertEquals("cid",

-                     column.getName());

-        assertEquals("CHAR",

-                     column.getType());

-        assertEquals(Types.CHAR,

-                     column.getTypeCode());

-        assertEquals("32",

-                     column.getSize());

-        assertEquals(32,

-                     column.getSizeAsInt());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The field for the foreign key towards C",

-                     column.getDescription());

+        assertEquals("B", "Table B", 3, 1, 0, 2, 1,

+                     table);

+        assertEquals("id", Types.TIMESTAMP, 0, 0, null, "The primary key of table B", null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("aid", Types.INTEGER, 0, 0, null, "The field for the foreign key towards A", null, false, false, false,

+                     table.getColumn(1));

+        assertEquals("cid", Types.CHAR, 32, 0, null, "The field for the foreign key towards C", null, false, false, false,

+                     table.getColumn(2));

 

         fk = table.getForeignKey(0);

 

-        assertNull(fk.getName());

-        assertEquals(model.getTable(0),

-                     fk.getForeignTable());

-        assertEquals("A",

-                     fk.getForeignTableName());

-        assertEquals(1,

-                     fk.getReferenceCount());

-

-        ref = fk.getFirstReference();

-

-        assertEquals(table.getColumn(1),

-                     ref.getLocalColumn());

-        assertEquals("aid",

-                     ref.getLocalColumnName());

-        assertEquals(model.getTable(0).getColumn(0),

-                     ref.getForeignColumn());

-        assertEquals("id",

-                     ref.getForeignColumnName());

+        assertEquals(null, CascadeActionEnum.NONE, CascadeActionEnum.NONE, model.getTable(0), 1, fk);

+        assertEquals(table.getColumn(1), model.getTable(0).getColumn(0), fk.getFirstReference());

 

         fk = table.getForeignKey(1);

 

-        assertNull(fk.getName());

-        assertEquals(model.getTable(2),

-                     fk.getForeignTable());

-        assertEquals("C",

-                     fk.getForeignTableName());

-        assertEquals(1,

-                     fk.getReferenceCount());

-

-        ref = fk.getFirstReference();

-

-        assertEquals(table.getColumn(2),

-                     ref.getLocalColumn());

-        assertEquals("cid",

-                     ref.getLocalColumnName());

-        assertEquals(model.getTable(2).getColumn(0),

-                     ref.getForeignColumn());

-        assertEquals("id",

-                     ref.getForeignColumnName());

+        assertEquals(null, CascadeActionEnum.NONE, CascadeActionEnum.NONE, model.getTable(2), 1, fk);

+        assertEquals(table.getColumn(2), model.getTable(2).getColumn(0), fk.getFirstReference());

 

         index = table.getIndex(0);

 

-        assertNull(index.getName());

-        assertFalse(index.isUnique());

-        assertEquals(2,

-                     index.getColumnCount());

-

-        indexColumn = index.getColumn(0);

-

-        assertEquals("aid",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

-

-        indexColumn = index.getColumn(1);

-

-        assertEquals("cid",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

+        assertEquals(null, false, 2, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

+        assertEquals(table.getColumn(2), null, index.getColumn(1));

 

         // table C

 

         table = model.getTable(2);

 

-        assertEquals("C",

-                     table.getName());

-        assertEquals("Table C",

-                     table.getDescription());

-        assertEquals(0, table.getAutoIncrementColumns().length);

-        assertEquals(2,

-                     table.getColumnCount());

-        assertEquals(0,

-                     table.getForeignKeyCount());

-        assertEquals(1,

-                     table.getIndexCount());

-

-        column = table.getColumn(0);

-

-        assertEquals("id",

-                     column.getName());

-        assertEquals("CHAR",

-                     column.getType());

-        assertEquals(Types.CHAR,

-                     column.getTypeCode());

-        assertEquals("32",

-                     column.getSize());

-        assertEquals(32,

-                     column.getSizeAsInt());

-        assertTrue(column.isPrimaryKey());

-        assertTrue(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The primary key of table C",

-                     column.getDescription());

-

-        column = table.getColumn(1);

-

-        assertEquals("text",

-                     column.getName());

-        assertEquals("LONGVARCHAR",

-                     column.getType());

-        assertEquals(Types.LONGVARCHAR,

-                     column.getTypeCode());

-        assertNull(column.getSize());

-        assertEquals(0,

-                     column.getSizeAsInt());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertEquals("The text",

-                     column.getDescription());

+        assertEquals("C", "Table C", 2, 1, 0, 0, 1,

+                     table);

+        assertEquals("id", Types.CHAR, 32, 0, null, "The primary key of table C", null, true, true, false,

+                     table.getColumn(0));

+        assertEquals("text", Types.LONGVARCHAR, 0, 0, null, "The text", null, false, false, false,

+                     table.getColumn(1));

 

         index = table.getIndex(0);

 

-        assertEquals("byText",

-                     index.getName());

-        assertFalse(index.isUnique());

-        assertEquals(1,

-                     index.getColumnCount());

-

-        indexColumn = index.getColumn(0);

-

-        assertEquals("text",

-                     indexColumn.getName());

-        assertNull(indexColumn.getSize());

+        assertEquals("byText", false, 1, index);

+        assertEquals(table.getColumn(1), null, index.getColumn(0));

 

         assertEquals(

-            "<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">\n" +

-            "  <database name=\"test\">\n" +

-            "    <table name=\"A\" description=\"Table A\">\n" +

-            "      <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"INTEGER\" autoIncrement=\"true\" description=\"The primary key of table A\"/>\n" +

-            "      <column name=\"parentId\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"The field for the foreign key parent\"/>\n" +

-            "      <column name=\"name\" primaryKey=\"false\" required=\"true\" type=\"VARCHAR\" size=\"32\" autoIncrement=\"false\" description=\"The name\"/>\n" +

-            "      <foreign-key foreignTable=\"A\" name=\"parent\">\n" +

-            "        <reference local=\"parentId\" foreign=\"id\"/>\n" +

-            "      </foreign-key>\n" +

-            "      <unique>\n" +

-            "        <unique-column name=\"name\"/>\n" +

-            "      </unique>\n" +

-            "    </table>\n" +

-            "    <table name=\"B\" description=\"Table B\">\n" +

-            "      <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"TIMESTAMP\" autoIncrement=\"false\" description=\"The primary key of table B\"/>\n" +

-            "      <column name=\"aid\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"The field for the foreign key towards A\"/>\n" +

-            "      <column name=\"cid\" primaryKey=\"false\" required=\"false\" type=\"CHAR\" size=\"32\" autoIncrement=\"false\" description=\"The field for the foreign key towards C\"/>\n" +

-            "      <foreign-key foreignTable=\"A\">\n" +

-            "        <reference local=\"aid\" foreign=\"id\"/>\n" +

-            "      </foreign-key>\n" +

-            "      <foreign-key foreignTable=\"C\">\n" +

-            "        <reference local=\"cid\" foreign=\"id\"/>\n" +

-            "      </foreign-key>\n" +

-            "      <index>\n" +

-            "        <index-column name=\"aid\"/>\n" +

-            "        <index-column name=\"cid\"/>\n" +

-            "      </index>\n" +

-            "    </table>\n" +

-            "    <table name=\"C\" description=\"Table C\">\n" +

-            "      <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"CHAR\" size=\"32\" autoIncrement=\"false\" description=\"The primary key of table C\"/>\n" +

-            "      <column name=\"text\" primaryKey=\"false\" required=\"false\" type=\"LONGVARCHAR\" autoIncrement=\"false\" description=\"The text\"/>\n" +

-            "      <index name=\"byText\">\n" +

-            "        <index-column name=\"text\"/>\n" +

-            "      </index>\n" +

-            "    </table>\n" +

-            "  </database>\n",

-            writeModel(model));

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"A\" description=\"Table A\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"INTEGER\" autoIncrement=\"true\" description=\"The primary key of table A\" />\n" +

+            "    <column name=\"parentId\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"The field for the foreign key parent\" />\n" +

+            "    <column name=\"name\" primaryKey=\"false\" required=\"true\" type=\"VARCHAR\" size=\"32\" autoIncrement=\"false\" description=\"The name\" />\n" +

+            "    <foreign-key foreignTable=\"A\" name=\"parent\">\n" +

+            "      <reference local=\"parentId\" foreign=\"id\" />\n" +

+            "    </foreign-key>\n" +

+            "    <unique>\n" +

+            "      <unique-column name=\"name\" />\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "  <table name=\"B\" description=\"Table B\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"TIMESTAMP\" autoIncrement=\"false\" description=\"The primary key of table B\" />\n" +

+            "    <column name=\"aid\" primaryKey=\"false\" required=\"false\" type=\"INTEGER\" autoIncrement=\"false\" description=\"The field for the foreign key towards A\" />\n" +

+            "    <column name=\"cid\" primaryKey=\"false\" required=\"false\" type=\"CHAR\" size=\"32\" autoIncrement=\"false\" description=\"The field for the foreign key towards C\" />\n" +

+            "    <foreign-key foreignTable=\"A\">\n" +

+            "      <reference local=\"aid\" foreign=\"id\" />\n" +

+            "    </foreign-key>\n" +

+            "    <foreign-key foreignTable=\"C\">\n" +

+            "      <reference local=\"cid\" foreign=\"id\" />\n" +

+            "    </foreign-key>\n" +

+            "    <index>\n" +

+            "      <index-column name=\"aid\" />\n" +

+            "      <index-column name=\"cid\" />\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "  <table name=\"C\" description=\"Table C\">\n" +

+            "    <column name=\"id\" primaryKey=\"true\" required=\"true\" type=\"CHAR\" size=\"32\" autoIncrement=\"false\" description=\"The primary key of table C\" />\n" +

+            "    <column name=\"text\" primaryKey=\"false\" required=\"false\" type=\"LONGVARCHAR\" autoIncrement=\"false\" description=\"The text\" />\n" +

+            "    <index name=\"byText\">\n" +

+            "      <index-column name=\"text\" />\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

     }

 

     /**

@@ -985,52 +2138,6 @@
     }

 

     /**

-     * Tests that an exception is generated when the database element has no name attribute.

-     */

-    public void testDatabaseWithoutName()

-    {

-        try

-        {

-            readModel(

-                "<database>\n" +

-                "  <table name='TestTable'>\n" +

-                "    <column name='id'\n" +

-                "            type='INTEGER'\n" +

-                "            primaryKey='true'\n" +

-                "            required='true'/>\n" +

-                "  </table>\n" +

-                "</database>");

-

-            fail();

-        }

-        catch (ModelException ex)

-        {}

-    }

-

-    /**

-     * Tests that an exception is generated when the table element has no name attribute.

-     */

-    public void testTableWithoutName()

-    {

-        try

-        {

-            readModel(

-                "<database name='test'>\n" +

-                "  <table>\n" +

-                "    <column name='id'\n" +

-                "            type='INTEGER'\n" +

-                "            primaryKey='true'\n" +

-                "            required='true'/>\n" +

-                "  </table>\n" +

-                "</database>");

-

-            fail();

-        }

-        catch (ModelException ex)

-        {}

-    }

-

-    /**

      * Tests that an exception is generated when two table elements have the same value in their name attributes.

      */

     public void testTwoTablesWithTheSameName()

@@ -1060,28 +2167,6 @@
     }

 

     /**

-     * Tests that an exception is generated when the column element has no name attribute.

-     */

-    public void testColumnWithoutName()

-    {

-        try

-        {

-            readModel(

-                "<database name='test'>\n" +

-                "  <table name='TestTable'>\n" +

-                "    <column type='INTEGER'\n" +

-                "            primaryKey='true'\n" +

-                "            required='true'/>\n" +

-                "  </table>\n" +

-                "</database>");

-

-            fail();

-        }

-        catch (ModelException ex)

-        {}

-    }

-

-    /**

      * Tests that an exception is generated when two column elements within the same table

      * element have the same value in their name attributes.

      */

@@ -1108,28 +2193,6 @@
     }

 

     /**

-     * Tests that an exception is generated when the column element has no type attribute.

-     */

-    public void testColumnWithoutType()

-    {

-        try

-        {

-            readModel(

-                "<database name='test'>\n" +

-                "  <table name='TestTable'>\n" +

-                "    <column name='id'\n" +

-                "            primaryKey='true'\n" +

-                "            required='true'/>\n" +

-                "  </table>\n" +

-                "</database>");

-

-            fail();

-        }

-        catch (ModelException ex)

-        {}

-    }

-

-    /**

      * Tests that an exception is generated when the a unique index references an undefined column.

      */

     public void testUndefinedUniqueColumn()

@@ -1272,7 +2335,6 @@
                 "  <index name='NotATable'/>\n" +

                 "</database>");

 

-        _log.debug("Table : " + Arrays.asList(database.getTables()));

         assertEquals(0, database.getTableCount());

     }

 

@@ -1287,70 +2349,30 @@
             "    <column name='intField'\n" +

             "            type='BOOLEANINT'/>\n" +

             "    <column name='charField'\n" +

-            "            type='BOOLEANCHAR'/>\n" +

+            "            type='BOOLEANCHAR' />\n" +

             "  </table>\n" +

             "</database>");

 

-        assertEquals("test",

-                     model.getName());

-        assertEquals(1,

-                     model.getTableCount());

+        assertEquals("test", model.getName());

+        assertEquals(1, model.getTableCount());

         

         Table table = model.getTable(0);

 

-        assertEquals("SomeTable",

-                     table.getName());

-        assertNull(table.getDescription());

-        assertEquals(0, table.getAutoIncrementColumns().length);

-        assertEquals(2,

-                     table.getColumnCount());

-        assertEquals(0,

-                     table.getForeignKeyCount());

-        assertEquals(0,

-                     table.getIndexCount());

-

-        Column column = table.getColumn(0);

-

-        assertEquals("intField",

-                     column.getName());

-        assertEquals("TINYINT",

-                     column.getType());

-        assertEquals(Types.TINYINT,

-                     column.getTypeCode());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDescription());

-

-        column = table.getColumn(1);

-

-        assertEquals("charField",

-                     column.getName());

-        assertEquals("CHAR",

-                     column.getType());

-        assertEquals(Types.CHAR,

-                     column.getTypeCode());

-        assertFalse(column.isPrimaryKey());

-        assertFalse(column.isRequired());

-        assertFalse(column.isAutoIncrement());

-        assertNull(column.getDefaultValue());

-        assertNull(column.getDescription());

+        assertEquals("SomeTable", null, 2, 0, 0, 0, 0,

+                     table);

+        assertEquals("intField", Types.TINYINT, 0, 0, null, null, null, false, false, false,

+                     table.getColumn(0));

+        assertEquals("charField", Types.CHAR, 0, 0, null, null, null, false, false, false,

+                     table.getColumn(1));

 

         assertEquals(

-            "<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + LocalEntityResolver.DTD_PREFIX + "\">\n" +

-            "  <database name=\"test\">\n" +

-            "    <table name=\"SomeTable\">\n" +

-            "      <column name=\"intField\" primaryKey=\"false\" required=\"false\" type=\"TINYINT\" autoIncrement=\"false\"/>\n" +

-            "      <column name=\"charField\" primaryKey=\"false\" required=\"false\" type=\"CHAR\" autoIncrement=\"false\"/>\n" +

-            "    </table>\n" +

-            "  </database>\n",

-            writeModel(model));

+            "<?xml version='1.0' encoding='UTF-8'?>\n" +

+            "<database name=\"test\">\n" +

+            "  <table name=\"SomeTable\">\n" +

+            "    <column name=\"intField\" primaryKey=\"false\" required=\"false\" type=\"TINYINT\" autoIncrement=\"false\" />\n" +

+            "    <column name=\"charField\" primaryKey=\"false\" required=\"false\" type=\"CHAR\" autoIncrement=\"false\" />\n" +

+            "  </table>\n" +

+            "</database>\n",

+            model);

     }

-

-    // TODO: Tests that include:

-    // * foreign key references undefined table

-    // * foreign key references undefined local column

-    // * foreign key references undefined foreign column

-    // * two foreign keys with the same name

 }