blob: b8f0cfac1ee1a7f933564dfff0dab6f5ebf6635d [file] [log] [blame]
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.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
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;
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;
/**
* This class provides functions to read and write database models from/to XML.
*
* @version $Revision$
*/
public class DatabaseIO
{
/** The name of the XML attribute use to denote that teh content of a data XML
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/ddlutils/schema/1.1";
/** 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 onDelete attribute. */
public static final QName QNAME_ATTRIBUTE_ON_DELETE = new QName(DDLUTILS_NAMESPACE, "onDelete");
/** Qualified name of the onUpdate attribute. */
public static final QName QNAME_ATTRIBUTE_ON_UPDATE = new QName(DDLUTILS_NAMESPACE, "onUpdate");
/** 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");
/** The log. */
private final Log _log = LogFactory.getLog(DatabaseIO.class);
/** 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.
*
* @return <code>true</code> if read XML is validated
*/
public boolean isValidateXml()
{
return _validateXml;
}
/**
* Specifies whether XML shall be validated upon reading it.
*
* @param validateXml <code>true</code> if read XML shall be validated
*/
public void setValidateXml(boolean validateXml)
{
_validateXml = validateXml;
}
/**
* Returns whether the internal dtd that comes with DdlUtils is used.
*
* @return <code>true</code> if parsing uses the internal dtd
* @deprecated Switched to XML schema, and the internal XML schema should always be used
*/
public boolean isUseInternalDtd()
{
return _useInternalDtd;
}
/**
* Specifies whether the internal dtd is to be used.
*
* @param useInternalDtd Whether to use the internal dtd
* @deprecated Switched to XML schema, and the internal XML schema should always be used
*/
public void setUseInternalDtd(boolean useInternalDtd)
{
_useInternalDtd = useInternalDtd;
}
/**
* Reads the database model contained in the specified file.
*
* @param filename The model file name
* @return The database model
*/
public Database read(String filename) throws DdlUtilsXMLException
{
return read(new File(filename));
}
/**
* Reads the database model contained in the specified file.
*
* @param file The model file
* @return The database model
*/
public Database read(File file) throws DdlUtilsXMLException
{
FileReader reader = null;
if (_validateXml)
{
try
{
reader = new FileReader(file);
new ModelValidator().validate(new StreamSource(reader));
}
catch (IOException ex)
{
throw new DdlUtilsXMLException(ex);
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException ex)
{
_log.warn("Could not close reader for file " + file.getAbsolutePath());
}
reader = null;
}
}
}
try
{
reader = new FileReader(file);
return read(getXMLInputFactory().createXMLStreamReader(reader));
}
catch (XMLStreamException ex)
{
throw new DdlUtilsXMLException(ex);
}
catch (IOException ex)
{
throw new DdlUtilsXMLException(ex);
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException ex)
{
_log.warn("Could not close reader for file " + file.getAbsolutePath());
}
}
}
}
/**
* Reads the database model given by the reader. Note that this method does not close the
* given reader.
*
* @param reader The reader that returns the model XML
* @return The database model
*/
public Database read(Reader reader) throws DdlUtilsXMLException
{
try
{
if (_validateXml)
{
StringBuffer tmpXml = new StringBuffer();
char[] buf = new char[4096];
int len;
while ((len = reader.read(buf)) >= 0)
{
tmpXml.append(buf, 0, len);
}
new ModelValidator().validate(new StreamSource(new StringReader(tmpXml.toString())));
return read(getXMLInputFactory().createXMLStreamReader(new StringReader(tmpXml.toString())));
}
else
{
return read(getXMLInputFactory().createXMLStreamReader(reader));
}
}
catch (XMLStreamException ex)
{
throw new DdlUtilsXMLException(ex);
}
catch (IOException ex)
{
throw new DdlUtilsXMLException(ex);
}
}
/**
* Reads the database model from the given input source.
*
* @param source The input source
* @return The database model
*/
public Database read(InputSource source) throws DdlUtilsXMLException
{
return read(source.getCharacterStream());
}
/**
* 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 DdlUtilsXMLException
{
Database model = null;
try
{
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 (IOException ex)
{
throw new DdlUtilsXMLException(ex);
}
catch (XMLStreamException ex)
{
throw new DdlUtilsXMLException(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.next();
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.next();
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(getAttributeValueBeingNullAware(xmlReader, 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));
}
else if (isSameAs(attrQName, QNAME_ATTRIBUTE_ON_UPDATE))
{
foreignKey.setOnUpdate(getAttributeValueAsCascadeEnum(xmlReader, idx));
}
else if (isSameAs(attrQName, QNAME_ATTRIBUTE_ON_DELETE))
{
foreignKey.setOnDelete(getAttributeValueAsCascadeEnum(xmlReader, 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.next();
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.next();
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.next();
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(getAttributeValueBeingNullAware(xmlReader, 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 string.
* This method can handle "null" in which case it returns a null object.
*
* @param xmlReader The xml reader
* @param attributeIdx The index of the attribute
* @return The attribute's value
*/
private String getAttributeValueBeingNullAware(XMLStreamReader xmlReader, int attributeIdx) throws DdlUtilsXMLException
{
String value = xmlReader.getAttributeValue(attributeIdx);
return "null".equalsIgnoreCase(value) ? null : value;
}
/**
* 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 DdlUtilsXMLException
{
String value = xmlReader.getAttributeValue(attributeIdx);
if ("true".equalsIgnoreCase(value))
{
return true;
}
else if ("false".equalsIgnoreCase(value))
{
return false;
}
else
{
throw new DdlUtilsXMLException("Illegal boolean value '" + value +"' for attribute " + xmlReader.getAttributeLocalName(attributeIdx));
}
}
/**
* 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 CascadeActionEnum getAttributeValueAsCascadeEnum(XMLStreamReader xmlReader, int attributeIdx) throws DdlUtilsXMLException
{
String value = xmlReader.getAttributeValue(attributeIdx);
CascadeActionEnum enumValue = value == null ? null : CascadeActionEnum.getEnum(value.toLowerCase());
if (enumValue == null)
{
throw new DdlUtilsXMLException("Illegal boolean value '" + value +"' for attribute " + xmlReader.getAttributeLocalName(attributeIdx));
}
else
{
return enumValue;
}
}
/**
* 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.next();
}
}
/**
* Reads over the current element. This assumes that the current XML stream event type is
* START_ELEMENT.
*
* @param reader The xml reader
*/
private void readOverElement(XMLStreamReader reader) throws XMLStreamException
{
int depth = 1;
while (depth > 0)
{
int eventType = reader.next();
if (eventType == XMLStreamReader.START_ELEMENT)
{
depth++;
}
else if (eventType == XMLStreamReader.END_ELEMENT)
{
depth--;
}
}
}
/**
* 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 DdlUtilsXMLException
{
try
{
BufferedWriter writer = null;
try
{
writer = new BufferedWriter(new FileWriter(filename));
write(model, writer);
writer.flush();
}
finally
{
if (writer != null)
{
writer.close();
}
}
}
catch (Exception ex)
{
throw new DdlUtilsXMLException(ex);
}
}
/**
* Writes the database model to the given output stream. Note that this method
* 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 DdlUtilsXMLException
{
write(model, new OutputStreamWriter(output));
}
/**
* Writes the database model to the given output writer. Note that this method
* 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 DdlUtilsXMLException
{
PrettyPrintingXmlWriter xmlWriter = new PrettyPrintingXmlWriter(output, "UTF-8");
xmlWriter.setDefaultNamespace(DDLUTILS_NAMESPACE);
xmlWriter.writeDocumentStart();
writeDatabaseElement(model, xmlWriter);
xmlWriter.writeDocumentEnd();
}
/**
* Writes the database model to the given XML writer.
*
* @param model The database model
* @param xmlWriter The XML writer
*/
private void writeDatabaseElement(Database model, PrettyPrintingXmlWriter xmlWriter) throws DdlUtilsXMLException
{
writeElementStart(xmlWriter, QNAME_ELEMENT_DATABASE);
xmlWriter.writeNamespace(null, DDLUTILS_NAMESPACE);
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)
{
xmlWriter.printlnIfPrettyPrinting();
for (int idx = 0; idx < model.getTableCount(); idx++)
{
writeTableElement(model.getTable(idx), xmlWriter);
}
}
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))
{
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.getOnUpdate() != CascadeActionEnum.NONE)
{
writeAttribute(xmlWriter, QNAME_ATTRIBUTE_ON_UPDATE, foreignKey.getOnUpdate().getName());
}
if (foreignKey.getOnDelete() != CascadeActionEnum.NONE)
{
writeAttribute(xmlWriter, QNAME_ATTRIBUTE_ON_DELETE, foreignKey.getOnDelete().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(qName.getNamespaceURI(), 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 (value != null)
{
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();
}
}