You are viewing a plain text version of this content. The canonical link for it is here.
Posted to slide-dev@jakarta.apache.org by cv...@apache.org on 2005/01/23 15:13:48 UTC
cvs commit: jakarta-slide/src/stores/org/apache/slide/store/ojb/tools DDLGeneratorTask.java SchemaReorderTask.java
cvillegas 2005/01/23 06:13:48
Added: src/stores/org/apache/slide/store/ojb/tools
DDLGeneratorTask.java SchemaReorderTask.java
Log:
Custom SQL generator to replace Torque.
Revision Changes Path
1.1 jakarta-slide/src/stores/org/apache/slide/store/ojb/tools/DDLGeneratorTask.java
Index: DDLGeneratorTask.java
===================================================================
/*
* $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/ojb/tools/DDLGeneratorTask.java,v 1.1 2005/01/23 14:13:48 cvillegas Exp $
* $Revision: 1.1 $
* $Date: 2005/01/23 14:13:48 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* 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 org.apache.slide.store.ojb.tools;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.util.StringUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import java.util.List;
import java.util.Vector;
import java.util.Iterator;
import java.util.HashSet;
import java.util.Properties;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.runtime.log.LogSystem;
import org.apache.velocity.runtime.RuntimeServices;
/**
* Generates a DDL script, the schema, for a given database from a
* Torque XML database schema.
* The script is generated using a Velocity template which is loaded from
* the classpath. The name of the velocity template is databasename + ".vm".
* A properties file databasename + ".properties" is also read from the classpath
* to set database specific settings. At the moment only the mappings from JDBC
* types to native types are used.
*
*/
public class DDLGeneratorTask extends Task implements LogSystem {
private Vector schemaFiles = new Vector();
private String outputPattern;
private Vector databases;
private HashSet seen = new HashSet();
private VelocityEngine engine;
private static final String DB = "%{database}";
private static final String IX = "%{index}";
private Vector elemTables = new Vector();
private long schemaLastModified = 0;
/**
* @param schema The database schema in Torque XML format
*/
public void setSchema(String schemaArg) {
Vector schema = StringUtils.split(schemaArg, ',');
for(int i=0; i<schema.size(); i++) {
String item = schema.get(i).toString().trim();
File sfile = new File(item);
if ( !sfile.isAbsolute() )
sfile = new File(getProject().getBaseDir(), item);
schemaFiles.add(sfile);
long lm = sfile.lastModified();
if ( lm > schemaLastModified )
schemaLastModified = lm;
}
}
/**
* The pattern to generate the output filename. The variables %{database}
* with the target database name and %{index} with the database index in the
* list of targets are substituted to generate the output filename.
*
* @param pattern sets the pattern for the generated output file name.
*/
public void setOutputPattern(String pattern) {
this.outputPattern = pattern;
}
/**
* use pattern to generate File for output
*/
private File getOutputFile(int index) {
String database = (String)databases.get(index);
String oname = StringUtils.replace(outputPattern, DB, database);
oname = StringUtils.replace(oname, IX, Integer.toString(index));
File ofile = new File(oname);
if ( !ofile.isAbsolute() )
ofile = new File(getProject().getBaseDir(), oname);
return ofile;
}
/**
* comma separated list of target databases.
*/
public void setTargetDatabases(String targets) {
if ( targets == null )
return;
databases = StringUtils.split(targets, ',');
}
/**
* Generate the DDL script for the target databases.
*
* @see org.apache.tools.ant.Task#execute()
*/
public void execute() throws BuildException {
if (schemaFiles.size() == 0 ) {
throw new BuildException( "Must specify an schema attribute" );
}
if (outputPattern == null) {
throw new BuildException( "Must specify an output attribute" );
}
if ( databases == null )
log("Nothing to do, no target databases");
try {
for(Iterator si=schemaFiles.iterator();si.hasNext();) {
File sfile = (File) si.next();
Document doc = new SAXBuilder().build(sfile);
List tables = doc.getRootElement().getChildren("table");
reorder(tables);
elemTables.addAll(tables);
}
initVelocity();
for (int i = 0; i < databases.size(); i++) {
processDatabase(i);
}
} catch (Exception e) {
e.printStackTrace();
throw new BuildException(e);
}
}
private void processDatabase(int di) throws BuildException {
String database = databases.get(di).toString().trim();
File output = getOutputFile(di);
try {
if ( output.exists() && output.lastModified() > schemaLastModified )
return;
log("Generating " + database + " DDL --> " + output.getName());
Properties props = loadProperties(database);
Vector tables = new Vector();
for(Iterator t=elemTables.iterator();t.hasNext();) {
Element elemTable = (Element)t.next();
Table table = new Table();
table.name = elemTable.getAttributeValue("name");
Index pk = null;
for(Iterator c=elemTable.getChildren("column").iterator();c.hasNext();) {
Element elemCol = (Element)c.next();
Column col = new Column();
col.name = elemCol.getAttributeValue("name");
Boolean isPK = Boolean.valueOf(elemCol.getAttributeValue("primaryKey"));
if ( isPK.booleanValue() ) {
if ( pk == null ) {
pk = new Index();
pk.name = table.name + "_PK";
}
pk.columns.add(col);
}
try {
col.size = Integer.parseInt(elemCol.getAttributeValue("size"));
} catch (Exception e) {
}
col.type = props.getProperty(elemCol.getAttributeValue("type"));
col.isNotNull = Boolean.valueOf(elemCol.getAttributeValue("required"));
table.columns.add(col);
}
table.primaryKey = pk;
for(Iterator fks=elemTable.getChildren("foreign-key").iterator(); fks.hasNext();) {
Element elemFk = (Element)fks.next();
ForeignKey fk = new ForeignKey();
fk.foreignTable = elemFk.getAttributeValue("foreignTable");
Element ref = elemFk.getChild("reference");
fk.localKey = ref.getAttributeValue("local");
fk.foreignKey = ref.getAttributeValue("foreign");
// sometime we have duplicate declarations
if ( findForeignKey(table.foreignKeys, fk.localKey) == null )
table.foreignKeys.add(fk);
}
for(Iterator ixs=elemTable.getChildren("unique").iterator();ixs.hasNext();) {
Element elemIndex = (Element)ixs.next();
Index index = new Index();
index.isUnique = true;
index.name = elemIndex.getAttributeValue("name");
for(Iterator ucs=elemIndex.getChildren("unique-column").iterator();ucs.hasNext();) {
Element elemCol = (Element)ucs.next();
Column col = findColumn(table.columns, elemCol.getAttributeValue("name"));
if ( col != null )
index.columns.add(col);
}
table.indices.add(index);
}
tables.add(table);
}
VelocityContext context = new VelocityContext();
context.put("eol", System.getProperty("line.separator"));
context.put("strings", new Strings());
context.put("tables", tables);
context.put("props", props);
Vector dropTables = new Vector();
for(int i=tables.size()-1; i>=0; i--) {
dropTables.add(tables.get(i));
}
context.put("droptables", dropTables);
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(output));
engine.mergeTemplate(database + ".vm", context, writer);
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
throw new BuildException(e);
}
}
private Properties loadProperties(String database) throws BuildException {
String propFile = database + ".properties";
try {
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/" + propFile));
return props;
} catch (Exception e) {
throw new BuildException(propFile + " not found", e);
}
}
private Column findColumn(List columns, String name) {
for(Iterator i=columns.iterator();i.hasNext();) {
Column col = (Column)i.next();
if ( col.name.equals(name) )
return col;
}
return null;
}
private ForeignKey findForeignKey(List keys, String localKey) {
for(Iterator i=keys.iterator();i.hasNext();) {
ForeignKey fk = (ForeignKey)i.next();
if ( fk.localKey.equals(localKey) )
return fk;
}
return null;
}
/**
* Reorder the table elements so that tables in foreign key
* constraints appear before tables that declares them.
*
* @param tables a JDOM list containing the table elements
*
*/
private void reorder(List tables) {
seen.clear();
for(int i=0; i<tables.size(); i++) {
i = reorderTable(tables, i);
}
}
private int reorderTable(List tables, int i) {
Element table = (Element)tables.get(i);
List fks = table.getChildren("foreign-key");
for (Iterator k=fks.iterator();k.hasNext();) {
Element fk = (Element)k.next();
String ft = fk.getAttributeValue("foreignTable");
if ( ! seen.contains(ft) ) {
Element dep = findTable(tables, i+1, ft);
if ( dep != null ) {
tables.add(i, dep.detach());
i = reorderTable(tables, i) + 1;
}
}
}
seen.add(table.getAttributeValue("name"));
return i;
}
private Element findTable(List tables, int start, String name) {
for(int i=start; i<tables.size();i++) {
Element table = (Element)tables.get(i);
if ( table.getAttributeValue("name").equals(name) )
return table;
}
return null;
}
// classes for passing data to Velocity template
public class Table {
String name;
Vector columns = new Vector();
Index primaryKey;
Vector indices = new Vector();
Vector foreignKeys = new Vector();
public String getName() {
return name;
}
public List getColumns() {
return columns;
}
public List getForeignKeys() {
return foreignKeys;
}
public List getIndices() {
return indices;
}
public Index getPrimaryKey() {
return primaryKey;
}
}
public class Column {
String name;
String type;
int size;
Boolean isNotNull = Boolean.FALSE;
public String getName() {
return name;
}
public String getType() {
return type;
}
public int getSize() {
return size;
}
public String printSize() {
String s = "";
if ( size > 0 )
s = "(" + size + ")";
return s;
}
public Boolean isNotNull() {
return isNotNull;
}
}
public class ForeignKey {
String localKey;
String foreignTable;
String foreignKey;
public String getLocalKey() {
return localKey;
}
public String getForeignTable() {
return foreignTable;
}
public String getForeignKey() {
return foreignKey;
}
}
public class Index {
String name;
Vector columns = new Vector();
boolean isUnique;
public String getName() {
return name;
}
public List getColumns() {
return columns;
}
public boolean isUnique() {
return isUnique;
}
}
public class Strings {
/**
* chops m characters from end of string after removing whitespace
*/
public String chop(String src, int m) {
src = trimRight(src);
if ( src.length()-m < 0 )
return src;
return src.substring(0, src.length()-m);
}
public String trimRight(String src) {
if ( src == null )
return src;
int i = src.length();
if ( i == 0 )
return src;
while(i>0) {
if ( !Character.isWhitespace(src.charAt(--i)) )
break;
}
return src.substring(0, i+1);
}
}
private void initVelocity() throws BuildException {
if ( engine != null )
return;
engine = new VelocityEngine();
engine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this);
engine.setProperty(VelocityEngine.PARSER_POOL_SIZE, new Integer(5));
// load templates from classpath
engine.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader" );
engine.setProperty(VelocityEngine.RESOURCE_LOADER, "class");
try {
engine.init();
} catch (Exception e) {
throw new BuildException("Could not initialize Velocity Engine", e);
}
}
// Velocity LogSystem implementation
public void init( RuntimeServices rs ) throws Exception {
}
public void logVelocityMessage(int level, String message)
{
switch (level) {
case LogSystem.WARN_ID:
log( message, Project.MSG_WARN );
break;
case LogSystem.INFO_ID:
// make Velocity quiet
log(message, Project.MSG_DEBUG);
break;
case LogSystem.DEBUG_ID:
log(message, Project.MSG_VERBOSE);
break;
case LogSystem.ERROR_ID:
log(message, Project.MSG_ERR);
break;
default:
log(message, Project.MSG_DEBUG);
break;
}
}
}
1.1 jakarta-slide/src/stores/org/apache/slide/store/ojb/tools/SchemaReorderTask.java
Index: SchemaReorderTask.java
===================================================================
/*
* $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/ojb/tools/SchemaReorderTask.java,v 1.1 2005/01/23 14:13:48 cvillegas Exp $
* $Revision: 1.1 $
* $Date: 2005/01/23 14:13:48 $
*
* ====================================================================
*
* Copyright 1999-2002 The Apache Software Foundation
*
* 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 org.apache.slide.store.ojb.tools;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import java.io.File;
import java.io.FileOutputStream;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
import org.jdom.input.SAXBuilder;
import java.util.List;
import java.util.Iterator;
import java.util.HashSet;
/**
* Reorder the table elements in the XML DDL schema so that tables in foreign key
* constraints appear before tables that declares them. This is required
* because Torque generates create statements in the order they appear in the
* schema file. Since this schema file is also generated, the order is undeterminate.
*
*/
public class SchemaReorderTask extends Task {
private File schema;
private File output;
private HashSet seen = new HashSet();
/**
* @return Returns the schema.
*/
public File getSchema() {
return schema;
}
/**
* @param schema The database schema in Torque XML format for reorder
*/
public void setSchema(File schema) {
this.schema = schema;
}
/**
* @return Returns the output.
*/
public File getOutput() {
return output;
}
/**
* @param output The output to set.
*/
public void setOutput(File output) {
this.output = output;
}
/**
* Checks that settings exist and in valid combinations
*
* @throws BuildException if parameters are incorrect
*/
private void assertValidSettings() throws BuildException {
if (schema == null ) {
throw new BuildException( "Must specify an schema attribute" );
}
if (output == null) {
throw new BuildException( "Must specify an output attribute" );
}
}
/**
* Reorder the table elements in the XML schema so that dependencies appear
* before the tables that require them.
*
* @see org.apache.tools.ant.Task#execute()
*/
public void execute() throws BuildException {
assertValidSettings();
try {
if ( output.exists() && output.lastModified() > schema.lastModified() )
return;
Document doc = new SAXBuilder().build(schema);
List tables = doc.getRootElement().getChildren("table");
for(int i=0; i<tables.size(); i++) {
i = doTable(tables, i);
}
XMLOutputter outp = new XMLOutputter();
outp.output(doc, new FileOutputStream(output));
} catch (Exception e) {
throw new BuildException(e);
}
}
private int doTable(List tables, int i) {
Element table = (Element)tables.get(i);
List fks = table.getChildren("foreign-key");
for (Iterator k=fks.iterator();k.hasNext();) {
Element fk = (Element)k.next();
String ft = fk.getAttributeValue("foreignTable");
if ( ! seen.contains(ft) ) {
Element dep = findTable(tables, i+1, ft);
if ( dep != null ) {
tables.add(i, dep.detach());
i = doTable(tables, i) + 1;
}
}
}
seen.add(table.getAttributeValue("name"));
return i;
}
private Element findTable(List tables, int start, String name) {
for(int i=start; i<tables.size();i++) {
Element table = (Element)tables.get(i);
if ( table.getAttributeValue("name").equals(name) )
return table;
}
return null;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: slide-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: slide-dev-help@jakarta.apache.org