validators) throws SAXException
    {
        if (validators.size() > 0)
        {
            simpleStartTag(XML.EL_VALIDATORS);
            for (DbValidator dbv : validators)
            {
                final AttributesImpl attribs = new AttributesImpl();
                attribs.addAttribute("", "", XML.ATTR_CLASS, "CDATA", dbv.getClass().getName());
                xmlOut.startElement("", "", XML.EL_VALIDATOR, attribs);
                
                if (dbv.getPropertyNames().size() > 0)
                {
                    simpleStartTag(XML.EL_PROPERTIES);
                    for (String propName : dbv.getPropertyNames())
                    {
                        final AttributesImpl propAttrs = new AttributesImpl();
                        propAttrs.addAttribute("", "", XML.ATTR_NAME, "CDATA", propName);
                        xmlOut.startElement("", "", XML.EL_PROPERTY, propAttrs);
                        String propValue = dbv.getProperty(propName);
                        char[] chars = propValue.toCharArray();
                        xmlOut.characters(chars, 0, chars.length);
                        simpleEndTag(XML.EL_PROPERTY);
                    }
                    simpleEndTag(XML.EL_PROPERTIES);
                }
                
                simpleEndTag(XML.EL_VALIDATOR);
            }
            simpleEndTag(XML.EL_VALIDATORS);
        }
    }
    /**
     * Add class-specific attributes.
     * 
     * @param dbObject DbObject
     * @param attribs AttributesImpl
     */
    private void addAttributes(DbObject dbObject, AttributesImpl attribs)
    {
        if (dbObject instanceof Schema)
        {
            Schema schema = (Schema) dbObject;
            attribs.addAttribute("", "", XML.ATTR_DB_PREFIX, "CDATA", schema.getDbPrefix());
            attribs.addAttribute("", "", XML.ATTR_VERSION, "CDATA", Integer.toString(schema.getVersion()));
            attribs.addAttribute("", "", XML.ATTR_TABLE_COLUMN_ORDER, "CDATA", Boolean.toString(schema.isCheckTableColumnOrder()));
        }
        else if (dbObject instanceof Index)
        {
            Index index = (Index) dbObject;
            attribs.addAttribute("", "", XML.ATTR_UNIQUE, "CDATA", Boolean.toString(index.isUnique()));
        }
        else if (dbObject instanceof Column)
        {
            Column column = (Column) dbObject;
            attribs.addAttribute("", "", XML.ATTR_ORDER, "CDATA", Integer.toString(column.getOrder()));
        }
    }
    private void transformDbObject(DbObject dbObject) throws SAXException
    {
        if (dbObject instanceof Schema)
        {
            transformSchema((Schema) dbObject);
        }
        else if (dbObject instanceof Table)
        {
            transformTable((Table) dbObject);
        }
        else if (dbObject instanceof Column)
        {
            transformColumn((Column) dbObject);
        }
        else if (dbObject instanceof ForeignKey)
        {
            transformForeignKey((ForeignKey) dbObject);
        }
        else if (dbObject instanceof Index)
        {
            transformIndex((Index) dbObject);
        }
        else if (dbObject instanceof PrimaryKey)
        {
            transformPrimaryKey((PrimaryKey) dbObject);
        }
    }
    
    
    private void transformSchema(Schema schema) throws SAXException
    {
        simpleStartTag(XML.EL_OBJECTS);
        for (DbObject dbo : schema)
        {
            output(dbo);
        }
        simpleEndTag(XML.EL_OBJECTS);
    }
    
    private void transformTable(Table table) throws SAXException
    {
        // Output columns
        simpleStartTag(XML.EL_COLUMNS);
        for (Column column : table.getColumns())
        {
            output(column);
        }
        simpleEndTag(XML.EL_COLUMNS);
        
        // Output primary key
        if (table.hasPrimaryKey())
        {
            output(table.getPrimaryKey());
        }
        
        // Output foreign keys
        simpleStartTag(XML.EL_FOREIGN_KEYS);
        for (ForeignKey fk : table.getForeignKeys())
        {
            output(fk);
        }
        simpleEndTag(XML.EL_FOREIGN_KEYS);
        // Output indexes
        simpleStartTag(XML.EL_INDEXES);
        for (Index index : table.getIndexes())
        {
            output(index);
        }
        simpleEndTag(XML.EL_INDEXES);
    }
    
    private void transformColumn(Column column) throws SAXException
    {
        simpleElement(XML.EL_TYPE, column.getType());
        simpleElement(XML.EL_NULLABLE, Boolean.toString(column.isNullable()));
        simpleElement(XML.EL_AUTOINCREMENT, Boolean.toString(column.isAutoIncrement()));
    }
    private void transformForeignKey(ForeignKey fk) throws SAXException
    {
        simpleElement(XML.EL_LOCAL_COLUMN, fk.getLocalColumn());
        simpleElement(XML.EL_TARGET_TABLE, fk.getTargetTable());
        simpleElement(XML.EL_TARGET_COLUMN, fk.getTargetColumn()); 
    }
    private void transformIndex(Index index) throws SAXException
    {
        columnNameList(index.getColumnNames(), null);
    }
    
    private void transformPrimaryKey(PrimaryKey pk) throws SAXException
    {
        columnNameList(pk.getColumnNames(), pk.getColumnOrders());
    }
    
    
    /**
     * Create a simple element of the form:
     * 
     *    <tag>content</tag>
     * 
     * 
     * @param tag String
     * @param content String
     * @throws SAXException
     */
    private void simpleElement(String tag, String content) throws SAXException
    {
        simpleStartTag(tag);
        char[] chars = content.toCharArray();
        xmlOut.characters(chars, 0, chars.length);
        simpleEndTag(tag);
    }
    
    private void simpleStartTag(String tag) throws SAXException
    {
        xmlOut.startElement("", "", tag, DbObjectXMLTransformer.EMPTY_ATTRIBUTES);
    }
    
    private void simpleEndTag(String tag) throws SAXException
    {
        xmlOut.endElement("", "", tag);        
    }
    
    /**
     * Outputs a list of columnname elements sandwiched within a columnnames element.
     * 
     * The columnOrders parameter will provide a corresponding list of integers that will be
     * provided in each columnname element's order attribute. This parameter may be null
     * in which case order attributes will be ommitted.
     *  
     * @param columnNames List
     * @param columnOrders List
     * @throws SAXException
     */
    private void columnNameList(List columnNames,
                                List columnOrders) throws SAXException
    {
        simpleStartTag(XML.EL_COLUMN_NAMES);
        for (int i = 0; i < columnNames.size(); i++)
        {
            String columnName = columnNames.get(i);
            
            final AttributesImpl attribs = new AttributesImpl();
            if (columnOrders != null)
            {
                int columnOrder = columnOrders.get(i);   
                attribs.addAttribute("", "", XML.ATTR_ORDER, "CDATA", Integer.toString(columnOrder));
            }
            // Create a  or  start tag
            xmlOut.startElement("", "", XML.EL_COLUMN_NAME, attribs);
            
            // Provide the elements content
            char[] chars = columnName.toCharArray();
            xmlOut.characters(chars, 0, chars.length);
            
            // Provide the closing tag
            simpleEndTag(XML.EL_COLUMN_NAME);
        }
        simpleEndTag(XML.EL_COLUMN_NAMES);
    }
}