package org.jumpmind.db.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 static org.apache.commons.lang.StringUtils.isNotBlank; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.util.Collection; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.IndexColumn; import org.jumpmind.db.model.NonUniqueIndex; import org.jumpmind.db.model.PlatformColumn; import org.jumpmind.db.model.Reference; import org.jumpmind.db.model.Table; import org.jumpmind.db.model.UniqueIndex; import org.jumpmind.exception.IoException; import org.jumpmind.util.FormatUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; /* * This class provides functions to read and write database models from/to XML. */ public class DatabaseXmlUtil { public static final String DTD_PREFIX = "http://db.apache.org/torque/dtd/database"; private DatabaseXmlUtil() { } /* * Reads the database model contained in the specified file. * * @param filename The model file name * * @return The database model */ public static Database read(String filename) { return read(new File(filename)); } /* * Reads the database model contained in the specified file. * * @param file The model file * * @return The database model */ public static Database read(File file) { FileReader reader = null; try { reader = new FileReader(file); return read(reader); } catch (IOException e) { throw new IoException(e); } finally { IOUtils.closeQuietly(reader); } } public static Database read(InputStream is) { try { return read(new InputStreamReader(is, "UTF-8")); } catch (IOException e) { throw new IoException(e); } } /* * Reads the database model given by the reader. * * @param reader The reader that returns the model XML * * @return The database model */ public static Database read(Reader reader) { return read(reader, true); } /* * Reads the database model given by the reader. * * @param reader The reader that returns the model XML * * @return The database model */ public static Database read(Reader reader, boolean validate) { try { boolean done = false; Database database = null; XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); parser.setInput(reader); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT && !done) { switch (eventType) { case XmlPullParser.START_DOCUMENT: database = new Database(); break; case XmlPullParser.START_TAG: String name = parser.getName(); if (name.equalsIgnoreCase("database")) { for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { database.setName(attributeValue); } else if (attributeName.equalsIgnoreCase("catalog")) { database.setCatalog(attributeValue); } else if (attributeName.equalsIgnoreCase("schema")) { database.setSchema(attributeValue); } } } else if (name.equalsIgnoreCase("table")) { Table table = nextTable(parser, database.getCatalog(), database.getSchema()); if (table != null) { database.addTable(table); } } break; case XmlPullParser.END_TAG: name = parser.getName(); if (name.equalsIgnoreCase("database")) { done = true; } break; } eventType = parser.next(); } if (validate) { database.initialize(); } return database; } catch (XmlPullParserException e) { throw new IoException(e); } catch (IOException e) { throw new IoException(e); } } public static Table nextTable(XmlPullParser parser) { return nextTable(parser, null, null); } public static Table nextTable(XmlPullParser parser, String catalog, String schema) { try { Table table = null; ForeignKey fk = null; IIndex index = null; boolean done = false; int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT && !done) { switch (eventType) { case XmlPullParser.START_TAG: String name = parser.getName(); if (name.equalsIgnoreCase("table")) { table = new Table(); table.setCatalog(catalog); table.setSchema(schema); for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { table.setName(attributeValue); } else if (attributeName.equalsIgnoreCase("description")) { table.setDescription(attributeValue); } } } else if (name.equalsIgnoreCase("column")) { Column column = new Column(); for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { column.setName(attributeValue); } else if (attributeName.equalsIgnoreCase("primaryKey")) { column.setPrimaryKey(FormatUtils.toBoolean(attributeValue)); } else if (attributeName.equalsIgnoreCase("required")) { column.setRequired(FormatUtils.toBoolean(attributeValue)); } else if (attributeName.equalsIgnoreCase("type")) { column.setMappedType(attributeValue); } else if (attributeName.equalsIgnoreCase("size")) { column.setSize(attributeValue); } else if (attributeName.equalsIgnoreCase("default")) { if (StringUtils.isNotBlank(attributeValue)) { column.setDefaultValue(attributeValue); } } else if (attributeName.equalsIgnoreCase("autoIncrement")) { column.setAutoIncrement(FormatUtils.toBoolean(attributeValue)); } else if (attributeName.equalsIgnoreCase("javaName")) { column.setJavaName(attributeValue); } else if (attributeName.equalsIgnoreCase("description")) { column.setDescription(attributeValue); } } if (table != null) { table.addColumn(column); } } else if (name.equalsIgnoreCase("platform-column")) { PlatformColumn platformColumn = new PlatformColumn(); for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { platformColumn.setName(attributeValue); } else if (attributeName.equalsIgnoreCase("type")) { platformColumn.setType(attributeValue); } else if (attributeName.equalsIgnoreCase("default")) { platformColumn.setDefaultValue(attributeValue); } else if (attributeName.equalsIgnoreCase("size")) { if (isNotBlank(attributeValue)) { platformColumn.setSize(Integer.parseInt(attributeValue)); } } else if (attributeName.equalsIgnoreCase("decimalDigits")) { if (isNotBlank(attributeValue)) { platformColumn.setDecimalDigits(Integer.parseInt(attributeValue)); } } } if (table != null && table.getColumnCount() > 0) { table.getColumn(table.getColumnCount()-1).addPlatformColumn(platformColumn); } } else if (name.equalsIgnoreCase("foreign-key")) { fk = new ForeignKey(); for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { fk.setName(attributeValue); } else if (attributeName.equalsIgnoreCase("foreignTable")) { fk.setForeignTableName(attributeValue); } } table.addForeignKey(fk); } else if (name.equalsIgnoreCase("reference")) { Reference ref = new Reference(); for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("local")) { ref.setLocalColumnName(attributeValue); } else if (attributeName.equalsIgnoreCase("foreign")) { ref.setForeignColumnName(attributeValue); } } fk.addReference(ref); } else if (name.equalsIgnoreCase("index") || name.equalsIgnoreCase("unique")) { if (name.equalsIgnoreCase("index")) { index = new NonUniqueIndex(); } else { index = new UniqueIndex(); } for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { index.setName(attributeValue); } } table.addIndex(index); } else if (name.equalsIgnoreCase("index-column") || name.equalsIgnoreCase("unique-column")) { IndexColumn indexColumn = new IndexColumn(); for (int i = 0; i < parser.getAttributeCount(); i++) { String attributeName = parser.getAttributeName(i); String attributeValue = parser.getAttributeValue(i); if (attributeName.equalsIgnoreCase("name")) { indexColumn.setName(attributeValue); } else if (attributeName.equalsIgnoreCase("size")) { indexColumn.setSize(attributeValue); } } indexColumn.setColumn(table.getColumnWithName(indexColumn.getName())); if (index != null) { index.addColumn(indexColumn); } } break; case XmlPullParser.END_TAG: name = parser.getName(); if (name.equalsIgnoreCase("table")) { done = true; } else if (name.equalsIgnoreCase("index") || name.equalsIgnoreCase("unique")) { index = null; } else if (name.equalsIgnoreCase("table")) { table = null; } else if (name.equalsIgnoreCase("foreign-key")) { fk = null; } break; } if (!done) { eventType = parser.next(); } } return table; } catch (XmlPullParserException e) { throw new IoException(e); } catch (IOException e) { throw new IoException(e); } } /* * Writes the database model to the specified file. * * @param model The database model * * @param filename The model file name */ public static void write(Database model, String filename) { try { BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(filename)); write(model, writer); writer.flush(); } finally { if (writer != null) { writer.close(); } } } catch (IOException ex) { throw new IoException(ex); } } /* * Writes the database model to the given output stream. Note that this * method does not flush the stream. * * @param model The database model * * @param output The output stream */ public static void write(Database model, OutputStream output) { Writer writer = new OutputStreamWriter(output); write(model, writer); try { writer.flush(); } catch (IOException e) { throw new IoException(e); } } /* * Writes the database model to the given output writer. Note that this * method does not flush the writer. * * @param model The database model * * @param output The output writer */ public static void write(Database model, Writer output) { try { output.write("<?xml version=\"1.0\"?>\n<!DOCTYPE database SYSTEM \"" + DTD_PREFIX + "\">\n"); output.write("<database name=\"" + model.getName() + "\""); if (model.getCatalog() != null) { output.write(" catalog=\"" + model.getCatalog() + "\""); } if (model.getSchema() != null) { output.write(" schema=\"" + model.getSchema() + "\""); } if (model.getIdMethod() != null) { output.write(" defaultIdMethod=\"" + model.getIdMethod() + "\""); } output.write(">\n"); for (Table table : model.getTables()) { write(table, output); } output.write("</database>\n"); } catch (IOException e) { throw new IoException(e); } } public static String toXml(Table table) { StringWriter writer = new StringWriter(); write(table, writer); return writer.toString(); } public static String toXml(Database db) { StringWriter writer = new StringWriter(); write(db, writer); return writer.toString(); } public static void write(Table table, Writer output) { try { output.write("\t<table name=\"" + StringEscapeUtils.escapeXml(table.getName()) + "\">\n"); for (Column column : table.getColumns()) { output.write("\t\t<column name=\"" + StringEscapeUtils.escapeXml(column.getName()) + "\""); if (column.isPrimaryKey()) { output.write(" primaryKey=\"" + column.isPrimaryKey() + "\""); } if (column.isRequired()) { output.write(" required=\"" + column.isRequired() + "\""); } if (column.getMappedType() != null) { output.write(" type=\"" + column.getMappedType() + "\""); } if (column.getSize() != null) { output.write(" size=\"" + column.getSize() + "\""); } if (column.getDefaultValue() != null) { output.write(" default=\"" + StringEscapeUtils.escapeXml(column.getDefaultValue()) + "\""); } if (column.isAutoIncrement()) { output.write(" autoIncrement=\"" + column.isAutoIncrement() + "\""); } if (column.getJavaName() != null) { output.write(" javaName=\"" + column.getJavaName() + "\""); } if (column.getPlatformColumns() != null && column.getPlatformColumns().size() > 0) { Collection<PlatformColumn> platformColumns = column.getPlatformColumns() .values(); output.write(">\n"); for (PlatformColumn platformColumn : platformColumns) { output.write("\t\t\t<platform-column name=\"" + platformColumn.getName() + "\""); output.write(" type=\"" + platformColumn.getType() + "\""); if (platformColumn.getSize() > 0) { output.write(" size=\"" + platformColumn.getSize() + "\""); } if (platformColumn.getDecimalDigits() > 0) { output.write(" decimalDigits=\"" + platformColumn.getDecimalDigits() + "\""); } if (platformColumn.getDefaultValue() != null) { output.write(" default=\"" + StringEscapeUtils.escapeXml(platformColumn.getDefaultValue()) + "\""); } output.write("/>\n"); } output.write("\t\t</column>\n"); } else { output.write("/>\n"); } } for (ForeignKey fk : table.getForeignKeys()) { output.write("\t\t<foreign-key name=\"" + StringEscapeUtils.escapeXml(fk.getName()) + "\" foreignTable=\"" + StringEscapeUtils.escapeXml(fk.getForeignTableName()) + "\">\n"); for (Reference ref : fk.getReferences()) { output.write("\t\t\t<reference local=\"" + StringEscapeUtils.escapeXml(ref.getLocalColumnName()) + "\" foreign=\"" + StringEscapeUtils.escapeXml(ref.getForeignColumnName()) + "\"/>\n"); } output.write("\t\t</foreign-key>\n"); } for (IIndex index : table.getIndices()) { if (index.isUnique()) { output.write("\t\t<unique name=\"" + StringEscapeUtils.escapeXml(index.getName()) + "\">\n"); for (IndexColumn column : index.getColumns()) { output.write("\t\t\t<unique-column name=\"" + StringEscapeUtils.escapeXml(column.getName()) + "\"/>\n"); } output.write("\t\t</unique>\n"); } else { output.write("\t\t<index name=\"" + StringEscapeUtils.escapeXml(index.getName()) + "\">\n"); for (IndexColumn column : index.getColumns()) { output.write("\t\t\t<index-column name=\"" + StringEscapeUtils.escapeXml(column.getName()) + "\""); if (column.getSize() != null) { output.write(" size=\"" + column.getSize() + "\""); } output.write("/>\n"); } output.write("\t\t</index>\n"); } } output.write("\t</table>\n"); } catch (IOException e) { throw new IoException(e); } } }