| 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 |
| { |
| 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 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(); |
| } |
| } |