Reveng.java

/**
 *
 */
package org.andromda.jdbcmetadata;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * Creates a hibernate reveng.xml file from JDBC metadata
 * @see "http://docs.redhat.com/docs/en-US/JBoss_Developer_Studio/3.0/html/Hibernate_Tools_Reference_Guide/hibernaterevengxmlfile.html"
 */
public class Reveng
{
    private static final Logger logger = Logger.getLogger(Reveng.class);
    private List<String> output = new ArrayList<String>();
    private String outputFile = "${basedir}\\reveng.xml";
    // TODO Set from configuration
    private String outputPackage; // = "org.andromda.persist.entity";
    private Populator pop;
    // Output documentation, inheritance info in reveng file
    private boolean metaTags = true;
    private String extendClasses;
    private String implementInterfaces;
    private List<String> extendList = new ArrayList<String>();
    private List<String> implementList = new ArrayList<String>();
    /**
     *
     */
    public Reveng()
    {
        // TODO Auto-generated constructor stub
    }

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        Reveng reveng = new Reveng();
        reveng.createReveng();
    }

    /**
     * Set the configuration values
     */
    // TODO Read configuration from file or command line
    public void setConfiguration()
    {

    }

    /**
     * @param populator Populator DB object to use
     */
    public void createReveng(Populator populator)
    {
        //this.extendList.add("org.andromda.persist.entity.AuditedEntity");
        //this.extendList.add("org.andromda.persist.AbstractAuditedVersionedEntity");
        validateConfiguration();
        this.pop = populator;
        this.pop.readOverrideConfiguration("${basedir}/override.properties");
        this.pop.populate();
        createHeader();
        createTypeMappings();
        createTableFilter();
        createTables();
        createFooter();
        outputFile();
    }

    /**
     */
    public void createReveng()
    {
        this.createReveng(new Populator());
    }

    /**
     */
    public void validateConfiguration()
    {
        if (StringUtils.isBlank(this.outputPackage))
        {
            logger.error("Reveng outputPackage not set");
            throw new RuntimeException("Reveng outputPackage not set");
        }
        if (StringUtils.isBlank(this.outputFile))
        {
            logger.error("Reveng outputFile not set");
            throw new RuntimeException("Reveng outputFile not set");
        }
    }

    /**
     *
     */
    public void outputFile()
    {
        try
        {
            FileUtils.writeLines(new File(this.outputFile), this.output);
            logger.info("Wrote " + this.output.size() + " lines to file " + this.outputFile);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     */
    public void createHeader()
    {
        this.output.add("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        this.output.add("<!-- See http://docs.jboss.org/tools/latest/en/hibernatetools/html/reverseengineering.html#hibernaterevengxmlfile");
        String date = new Date().toString();
        this.output.add("Generated by andromda schema2uml2 jdbcmetadata on " + date + " . -->");
        this.output.add("<!DOCTYPE hibernate-reverse-engineering ");
        // Official hibernate dtd does not allow meta tags even though hibernate-tools processes them
        this.output.add("  SYSTEM \"http://seaminaction.googlecode.com/svn/demos/articles/edas2-perflab/resources/hibernate-reverse-engineering-3.0.dtd\">");
        //this.output.add("  SYSTEM \"http://www.hibernate.org/dtd/hibernate-reverse-engineering-3.0.dtd\">");
        this.output.add("");
        this.output.add("<hibernate-reverse-engineering>");
    }

    /**
     */
    public void createTypeMappings()
    {
        this.output.add(" <schema-selection match-schema=\"PUBLIC\" />");
        this.output.add(" <type-mapping>");
        this.output.add("  <!-- This maps all numerics with precision 0 to Long, instead of primitive long or BigDecimal -->");
        this.output.add("  <!-- jdbc-type is name for java.sql.Types -->");
        this.output.add("  <!-- length, scale and precision can be used to specify the mapping precisely -->");
        this.output.add("  <!-- type-mappings are ordered. Later mappings will be consulted last, thus overriding the previous one -->");
        this.output.add("  <sql-type jdbc-type=\"NUMERIC\" hibernate-type=\"java.lang.Long\"/>");
        this.output.add("  <sql-type jdbc-type=\"BIGINT\" hibernate-type=\"java.lang.Long\"/>");
        this.output.add("  <sql-type jdbc-type=\"DECIMAL\" scale=\"0\" hibernate-type=\"java.lang.Long\"/>");
        this.output.add(" </type-mapping>");
    }

    /**
     * Create table-filter output in reveng file
     */
    public void createTableFilter()
    {
        this.output.add("");
        this.output.add(" <!-- BIN$ is recycle bin tables in Oracle. -->");
        this.output.add(" <table-filter match-name=\"BIN$.*\" exclude=\"true\" />");
        for (String exclude : this.pop.getTableNameExcludePatterns())
        {
            this.output.add(" <table-filter match-name=\"" + exclude + "\" exclude=\"true\" />");
        }
        this.output.add(" <table-filter match-catalog=\"" + this.pop.getSchema().getCatalog() +
            "\" match-name=\"" + this.pop.getSchema().getTableNamePattern() +
            "\" package=\"" + this.outputPackage + "\" />");
        this.output.add("");
    }

    /**
     * Create table output in reveng file
     */
    public void createTables()
    {
        // Put extends/implements declaration in file once before all table declarations
        for (Table table : this.pop.getTables())
        {
            if (!table.isExcluded())
            {
                this.output.add("  <table name=\"" + table.getName() +
                    "\" class=\"" + table.getUmlName() +
                    "\" schema=\"" + this.pop.getSchema().getSchemaName() +
                    "\" catalog=\"" + this.pop.getSchema().getCatalog() + "\" >");
                if (this.metaTags && StringUtils.isNotBlank(table.getDescription()))
                {
                    this.output.add("    <meta attribute=\"class-description\">" + table.getDescription() + "</meta>");
                    for (String extend : this.extendList)
                    {
                        this.output.add("    <meta attribute=\"extends\">" + extend + "</meta>");
                    }
                    for (String implement : this.implementList)
                    {
                        this.output.add("    <meta attribute=\"implements\">" + implement + "</meta>");
                    }
                }
                createSequence(table);
                createColumns(table);
                this.output.add("  </table>");
            }
        }
    }

    /**
     * Add Column definitions for the table
     * @param table
     */
    public void createSequence(Table table)
    {
        for (Column column : table.getColumns())
        {
            if (column.pkSeqNum > 0 && StringUtils.isNotBlank(column.getGeneratorName()))
            {

            }
        }
    }

    /**
     * Add primary-key annotation if column is a numeric primary key
     * @param table Table to create columns
     */
    public void createColumns(Table table)
    {
        for (Column column : table.getColumns())
        {
            String columnElement = "    <column name=\"" + column.getColumnName() + "\"" +
            (StringUtils.isNotBlank(column.getUmlName()) ? " property=\"" + column.getUmlName() + "\"" : "") +
            (column.isExcluded()? " exclude=\"" + column.isExcluded() + "\"" : "");
            String description = column.getDescription();
            // TODO If no description and the column is a FK association, use the FK table description
            if (this.metaTags && StringUtils.isNotBlank(description))
            {
                this.output.add(columnElement + " >");
                this.output.add("      <meta attribute=\"field-description\">" + description + "</meta>");
                this.output.add("    </column>");
            }
            else
            {
                this.output.add(columnElement + " />");
            }
        }
    }

    /**
     */
    public void createFooter()
    {
        this.output.add("</hibernate-reverse-engineering>");
    }

    /**
     * @return the output
     */
    public List<String> getOutput()
    {
        return this.output;
    }

    /**
     * @param output the output to set
     */
    public void setOutput(List<String> output)
    {
        this.output = output;
    }

    /**
     * @return the outputFile
     */
    public String getOutputFile()
    {
        return this.outputFile;
    }

    /**
     * @param outputFile the outputFile to set
     */
    public void setOutputFile(String outputFile)
    {
        this.outputFile = outputFile;
    }

    /**
     * @return the outputPackage
     */
    public String getOutputPackage()
    {
        return this.outputPackage;
    }

    /**
     * @param outputPackage the outputPackage to set
     */
    public void setOutputPackage(String outputPackage)
    {
        this.outputPackage = outputPackage;
    }

    /**
     * @return the metaTags
     */
    public boolean isMetaTags()
    {
        return this.metaTags;
    }

    /**
     * @param metaTags the metaTags to set
     */
    public void setMetaTags(boolean metaTags)
    {
        this.metaTags = metaTags;
    }

    /**
     * @return the extendList
     */
    public List<String> getExtendList()
    {
        return this.extendList;
    }

    /**
     * @param extendList the extendList to set
     */
    public void setExtendList(List<String> extendList)
    {
        this.extendList = extendList;
    }

    /**
     * @return the implementList
     */
    public List<String> getImplementList()
    {
        return this.implementList;
    }

    /**
     * @param implementList the implementList to set
     */
    public void setImplementList(List<String> implementList)
    {
        this.implementList = implementList;
    }

    /**
     * @return the extendClasses
     */
    public String getExtendClasses()
    {
        return this.extendClasses;
    }

    /**
     * @param extendClasses the extendClasses to set
     */
    public void setExtendClasses(String extendClasses)
    {
        this.extendClasses = extendClasses;
        if (StringUtils.isNotBlank(extendClasses))
        {
            String[] classes = StringUtils.split(extendClasses, " ,|");
            for (String extend : classes)
            {
                if (!this.extendList.contains(extend))
                {
                    this.extendList.add(extend);
                }
            }
        }
    }

    /**
     * @return the implementInterfaces
     */
    public String getImplementInterfaces()
    {
        return this.implementInterfaces;
    }

    /**
     * @param implementInterfaces the implementInterfaces to set
     */
    public void setImplementInterfaces(String implementInterfaces)
    {
        this.implementInterfaces = implementInterfaces;
        if (StringUtils.isNotBlank(implementInterfaces))
        {
            String[] classes = StringUtils.split(implementInterfaces, " ,|");
            for (String impl : classes)
            {
                if (!this.implementList.contains(impl))
                {
                    this.implementList.add(impl);
                }
            }
        }
    }

    /**
     * @param pop the pop to set
     */
    public void setPop(Populator pop)
    {
        this.pop = pop;
    }
}