/* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed 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. * !# */ package net.ontopia.persistence.rdbms; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.ontopia.utils.OntopiaRuntimeException; import net.ontopia.utils.StreamUtils; import net.ontopia.utils.StringUtils; import net.ontopia.xml.DefaultXMLReaderFactory; import net.ontopia.xml.PrettyPrinter; import net.ontopia.xml.SAXTracker; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.AttributesImpl; /** * INTERNAL: Class that can read a database schema definition from an * XML representation. */ public class DatabaseProjectReader { protected static final AttributesImpl EMPTY_ATTR_LIST = new AttributesImpl(); protected static final String EMPTY_NAMESPACE = ""; protected static final String EMPTY_LOCALNAME = ""; private DatabaseProjectReader() { } /** * INTERNAL: Reads the database schema definition from the specified file. */ public static Project loadProject(String filename) throws IOException, SAXException { return loadProject(StreamUtils.getInputStream(filename)); } public static Project loadProject(InputStream istream) throws IOException, SAXException { return loadProject(new InputSource(istream)); } public static Project loadProject(InputSource isource) throws IOException, SAXException { ProjectHandler handler = new ProjectHandler(); XMLReader parser = DefaultXMLReaderFactory.createXMLReader(); parser.setContentHandler(handler); parser.parse(isource); return handler.project; } public static void saveProject(Project project, String filename) throws IOException, SAXException { saveProject(project, filename, "utf-8"); } public static void saveProject(Project project, String filename, String encoding) throws IOException, SAXException { PrintWriter print = new PrintWriter(new FileWriter(filename)); saveProject(project, new PrettyPrinter(print, encoding)); print.close(); } public static void saveProject(Project project, ContentHandler dh) throws SAXException { AttributesImpl atts = new AttributesImpl(); dh.startDocument(); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "dbschema", EMPTY_ATTR_LIST); Iterator<String> platforms = project.getDataTypePlatforms().iterator(); if (platforms.hasNext()) { while (platforms.hasNext()) { String platform = platforms.next(); atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "platform", "CDATA", platform); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "datatypes", atts); Iterator<DataType> datatypes = project.getDataTypes(platform).iterator(); while (datatypes.hasNext()) { // Platform datatypes DataType datatype = datatypes.next(); atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "name", "CDATA", (datatype.getName())); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "type", "CDATA", (datatype.getType())); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "size", "CDATA", (datatype.getSize() == null ? "" : datatype.getSize())); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "class", "CDATA", (datatype.isVariable() ? "variable" : "constant")); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "datatype", atts); // Datatype properties Iterator<String> properties = datatype.getProperties().iterator(); while (properties.hasNext()) { String name = properties.next(); String value = datatype.getProperty(name); if (value != null) { atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "name", "CDATA", name); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "value", "CDATA", value); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "property", atts); dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "property"); } } dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "datatype"); } } dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "datatypes"); } Iterator<Table> tables = project.getTables().iterator(); while (tables.hasNext()) { Table table = tables.next(); // Table attributes atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "name", "CDATA", table.getName()); if (table.getShortName() != null) atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "short", "CDATA", table.getShortName()); if (table.getPrimaryKeys() != null) atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "pks", "CDATA", StringUtils.join(table.getPrimaryKeys(), " ")); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "table", atts); // Table properties Iterator<String> properties = table.getProperties().iterator(); while (properties.hasNext()) { String name = properties.next(); String value = table.getProperty(name); if (value != null) { atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "name", "CDATA", name); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "value", "CDATA", value); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "property", atts); dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "property"); } } Iterator<Column> columns = table.getColumns().iterator(); while (columns.hasNext()) { Column column = columns.next(); // Column attributes atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "name", "CDATA", column.getName()); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "type", "CDATA", column.getType()); if (column.isReference()) { atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "reftab", "CDATA", column.getReferencedTable()); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "refcol", "CDATA", column.getReferencedColumn()); } if (column.getSize() != null) atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "size", "CDATA", column.getName()); if (column.isNullable()) atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "null", "CDATA", "yes"); if (column.getDefault() != null) atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "default", "CDATA", column.getDefault()); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "column", atts); // Column properties Iterator<String> properties2 = column.getProperties().iterator(); while (properties2.hasNext()) { String name = properties2.next(); String value = column.getProperty(name); if (value != null) { atts.clear(); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "name", "CDATA", name); atts.addAttribute(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "value", "CDATA", value); dh.startElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "property", atts); dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "property"); } } dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "column"); } dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "table"); } dh.endElement(EMPTY_NAMESPACE, EMPTY_LOCALNAME, "dbschema"); dh.endDocument(); } static class ProjectHandler extends SAXTracker { static final String EL_DBSCHEMA = "dbschema"; static final String EL_DATATYPES = "datatypes"; static final String EL_DATATYPE = "datatype"; static final String EL_TABLE = "table"; static final String EL_COLUMN = "column"; static final String EL_INDEX = "index"; static final String EL_PROPERTY = "property"; static final String EL_CREATE_ACTION = "create-action"; static final String EL_DROP_ACTION = "drop-action"; protected Project project; protected Map<String, Object> info; public ProjectHandler() { keepContentsOf("create-action"); keepContentsOf("drop-action"); } public void startDocument() { project = new Project(); info = new HashMap<String, Object>(); } public void endDocument() { info = null; } public void startElement(String uri, String name, String qname, Attributes atts) throws SAXException { super.startElement(uri, name, qname, atts); // System.out.println("S: " + name + ":" + openElements); if (qname == EL_COLUMN) { // Instantiate new column instance Column column = new Column(); String cname = atts.getValue("name"); if (cname == null) throw new OntopiaRuntimeException("column.name must be specified: " + cname); column.setName(cname); String type = atts.getValue("type"); if (type == null) throw new OntopiaRuntimeException("column.type must be specified: " + cname); // if (project.getDataTypeByName(type) == null) // throw new OntopiaRuntimeException("Unknown datatype:: " + type); column.setType(type); String size = atts.getValue("size"); if (size != null) column.setSize(size); String default_value = atts.getValue("default"); if (default_value != null) column.setDefault(default_value); String reftable = atts.getValue("reftab"); if (reftable != null) column.setReferencedTable(reftable); String refcol = atts.getValue("refcol"); if ((refcol == null && reftable != null) || (refcol != null && reftable == null)) throw new OntopiaRuntimeException("column.refcol and reftable must both specified:" + cname); if (refcol != null) column.setReferencedColumn(refcol); String nullable = atts.getValue("null"); if (nullable == null || nullable.equals("no")) column.setNullable(false); else column.setNullable(true); Table table = (Table)info.get(EL_TABLE); // Add column to table table.addColumn(column); info.put(EL_COLUMN, column); } else if (qname == EL_INDEX) { // Instantiate new index instance Index index = new Index(); String iname = atts.getValue("name"); if (iname == null) throw new OntopiaRuntimeException("index.name must be specified: " + iname); index.setName(iname); String isname = atts.getValue("short"); index.setShortName(isname); String icolumns = atts.getValue("columns"); if (icolumns == null) throw new OntopiaRuntimeException("index.columns must be specified: " + icolumns); index.setColumns(StringUtils.split(icolumns, ",")); Table table = (Table)info.get(EL_TABLE); // Add index to table table.addIndex(index); info.put(EL_INDEX, index); } else if (qname == EL_TABLE) { // Instantiate new table instance Table table = new Table(); String tname = atts.getValue("name"); if (tname == null) throw new OntopiaRuntimeException("table.name must be specified: " + tname); table.setName(tname); String tsname = atts.getValue("short"); table.setShortName(tsname); String pkeys = atts.getValue("pks"); if (pkeys != null) table.setPrimaryKeys(StringUtils.split(pkeys, " ")); // Add table to project project.addTable(table); info.put(EL_TABLE, table); } else if (qname == EL_DATATYPES) { String platform = atts.getValue("platform"); if (platform == null) throw new OntopiaRuntimeException("datatypes.platform must be specified: " + platform); info.put(EL_DATATYPES, platform); } else if (qname == EL_DATATYPE) { // Instantiate new datatype instance DataType datatype = new DataType(); String platform = (String)info.get(EL_DATATYPES); String dname = atts.getValue("name"); if (dname == null) throw new OntopiaRuntimeException("datatype.name must be specified: " + dname); datatype.setName(dname); String type = atts.getValue("type"); if (type == null) throw new OntopiaRuntimeException("datatype.type must be specified: " + dname); datatype.setType(type); String klass = atts.getValue("class"); if (klass == null || klass.equals("variable")) datatype.setVariable(true); else datatype.setVariable(false); String size = atts.getValue("size"); if (datatype.isVariable()) { if (size == null) throw new OntopiaRuntimeException("datatype.size must be specified: " + dname); datatype.setSize(size); } // Add table to project project.addDataType(datatype, platform); info.put(EL_DATATYPE, datatype); } else if (qname == EL_PROPERTY) { if (info.containsKey(EL_DATATYPE)) { String propname = atts.getValue("name"); if (propname == null) throw new OntopiaRuntimeException("property.name must be specified."); String value = atts.getValue("value"); if (value == null) throw new OntopiaRuntimeException("property.value must be specified."); DataType datatype = (DataType)info.get(EL_DATATYPE); datatype.addProperty(propname, value); } else if (info.containsKey(EL_COLUMN)) { String propname = atts.getValue("name"); if (propname == null) throw new OntopiaRuntimeException("property.name must be specified."); String value = atts.getValue("value"); if (value == null) throw new OntopiaRuntimeException("property.value must be specified."); Column column = (Column)info.get(EL_COLUMN); column.addProperty(propname, value); } else if (info.containsKey(EL_TABLE)) { String propname = atts.getValue("name"); if (propname == null) throw new OntopiaRuntimeException("property.name must be specified."); String value = atts.getValue("value"); if (value == null) throw new OntopiaRuntimeException("property.value must be specified."); Table table = (Table)info.get(EL_TABLE); table.addProperty(propname, value); } else throw new OntopiaRuntimeException("property element in unknown parent." + info); } else if (qname == EL_CREATE_ACTION) { String platform = atts.getValue("platform"); if (platform == null) throw new OntopiaRuntimeException("create-action.platform must be specified: " + platform); info.put(EL_CREATE_ACTION, platform); } else if (qname == EL_DROP_ACTION) { String platform = atts.getValue("platform"); if (platform == null) throw new OntopiaRuntimeException("drop-action.platform must be specified: " + platform); info.put(EL_DROP_ACTION, platform); } else if (qname == EL_DBSCHEMA) { } } public void endElement(String uri, String name, String qname) throws SAXException { super.endElement(uri, name, qname); if (qname == EL_DATATYPES) { // Remove types entry info.remove(EL_DATATYPES); } else if (qname == EL_DATATYPE) { // Remove type entry info.remove(EL_DATATYPE); } else if (qname == EL_TABLE) { // Remove table entry info.remove(EL_TABLE); } else if (qname == EL_COLUMN) { // Remove column entry info.remove(EL_COLUMN); } else if (qname == EL_INDEX) { // Remove index entry info.remove(EL_INDEX); } else if (qname == EL_CREATE_ACTION) { project.addCreateAction((String)info.get(EL_CREATE_ACTION), content.toString()); // Remove table entry info.remove(EL_CREATE_ACTION); } else if (qname == EL_DROP_ACTION) { project.addDropAction((String)info.get(EL_DROP_ACTION), content.toString()); // Remove table entry info.remove(EL_DROP_ACTION); } else if (qname == EL_PROPERTY) { } else if (qname == EL_DBSCHEMA) { } else if (qname == EL_INDEX) { } else { System.out.println("Ignoring: " + name); } // System.out.println("E: " + name); } protected Map<String, String> parseAttribs(String content) { Map<String, String> result = new HashMap<String, String>(); String[] fields = StringUtils.split(content.toString(), "\n"); for (int i=0; i < fields.length; i++) { String field = fields[i]; String[] entry = StringUtils.split(field, "="); if (entry.length != 2) { // System.out.println("Ignoring: '" + field + "' (" + entry.length + ")"); continue; } result.put(entry[0], entry[1]); } return result; } } public static void main(String[] args) throws IOException, SAXException { DatabaseProjectReader preader = new DatabaseProjectReader(); preader.loadProject(args[0]); } }