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