edu.ku.brc.specify.tools.datamodelgenerator.DatamodelGenerator.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.specify.tools.datamodelgenerator.DatamodelGenerator.java

Source

/* Copyright (C) 2015, University of Kansas Center for Research
 * 
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.specify.tools.datamodelgenerator;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.Vector;

import javax.persistence.CascadeType;

import org.apache.commons.betwixt.XMLIntrospector;
import org.apache.commons.betwixt.io.BeanWriter;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.annotations.Index;

import edu.ku.brc.af.core.SchemaI18NService;
import edu.ku.brc.af.core.db.DBTableIdMgr;
import edu.ku.brc.af.ui.db.PickListItemIFace;
import edu.ku.brc.dbsupport.AttributeIFace;
import edu.ku.brc.dbsupport.RecordSetItemIFace;
import edu.ku.brc.helpers.XMLHelper;
import edu.ku.brc.specify.datamodel.SpLocaleContainer;
import edu.ku.brc.specify.tools.schemalocale.LocalizableContainerIFace;
import edu.ku.brc.specify.tools.schemalocale.LocalizableItemIFace;
import edu.ku.brc.specify.tools.schemalocale.LocalizableStrIFace;
import edu.ku.brc.specify.tools.schemalocale.LocalizerBasePanel;
import edu.ku.brc.specify.tools.schemalocale.SchemaLocalizerXMLHelper;
import edu.ku.brc.util.DatamodelHelper;

/**
 * This generates the specify datamodel file
 * 
 * @code_status Alpha
 * 
 * @author rods
 * 
 */
public class DatamodelGenerator {
    enum RelType {
        OneToMany, OneToOne, ManyToOne, ManyToMany, ZeroOrOne
    }

    private static final Logger log = Logger.getLogger(DatamodelGenerator.class);

    protected static final boolean DEBUG = false;

    protected Hashtable<String, TableMetaData> tblMetaDataHash = new Hashtable<String, TableMetaData>();

    //protected Vector<SpLocaleContainer> descTableList = new Vector<SpLocaleContainer>();

    protected File srcCodeDir = null;
    protected String packageName = null;
    protected int missing = 0;

    protected SchemaLocalizerXMLHelper schemaLocalizer = null;

    protected Hashtable<String, String> abbrvHash = new Hashtable<String, String>();

    protected boolean includeDesc = false;
    protected boolean doRelsToZeroToOne = true;
    protected boolean doPT = true;
    protected boolean showDescErrors = false;
    protected boolean showDebug = false;

    /**
     * 
     */
    public DatamodelGenerator(final boolean includeDesc) {
        this.includeDesc = includeDesc;

        readDescriptions();
    }

    protected void readDescriptions() {
        if (includeDesc) {
            if (doPT) {
                SchemaI18NService.setCurrentLocale(new Locale("pt", "", ""));
            }

            schemaLocalizer = new SchemaLocalizerXMLHelper(SpLocaleContainer.CORE_SCHEMA,
                    DBTableIdMgr.getInstance());
            includeDesc = schemaLocalizer.load(true);

            //descTableList = schemaLocalizer.getSpLocaleContainers();
        }
    }

    /**
     * @param tableName
     * @return
     */
    protected Desc getTableDesc(final String tableName) {
        LocalizableContainerIFace container = schemaLocalizer.getContainer(tableName);
        if (container != null) {
            LocalizableStrIFace d = LocalizerBasePanel.getDescForCurrLocale(container);
            if (d != null) {
                Desc desc = new Desc(d.getText(), d.getCountry(), d.getLanguage(), d.getVariant());
                return desc;
            }
            if (showDescErrors)
                log.error("No Desc for Table[" + tableName + "]");
        } else {
            log.error("No Table[" + tableName + "]");
        }

        return null;
    }

    /**
     * @param tableName
     * @return
     */
    protected Name getTableNameDesc(final String tableName) {
        LocalizableContainerIFace container = schemaLocalizer.getContainer(tableName);
        if (container != null) {
            LocalizableStrIFace dn = LocalizerBasePanel.getNameDescForCurrLocale(container);
            if (dn != null) {
                Name nm = new Name(dn.getText(), dn.getCountry(), dn.getLanguage(), dn.getVariant());
                return nm;
            }
            log.error("No NameDesc for [" + tableName + "]");
        } else {
            log.error("No Table[" + tableName + "]");
        }
        return null;
    }

    /**
     * @param tableName
     * @return
     */
    protected Desc getFieldDesc(final String tableName, final String fieldName) {
        LocalizableContainerIFace container = schemaLocalizer.getContainer(tableName);
        if (container != null) {
            LocalizableItemIFace item = container.getItemByName(fieldName);
            if (item != null) {
                LocalizableStrIFace d = LocalizerBasePanel.getDescForCurrLocale(item);
                if (d != null) {
                    Desc desc = new Desc(d.getText(), d.getCountry(), d.getLanguage(), d.getVariant());
                    return desc;
                }
                if (showDescErrors)
                    log.error("No Desc for [" + tableName + "] Field[" + fieldName + "]");
            } else {
                log.error("No Field [" + tableName + "] Field[" + fieldName + "]");
            }
        } else {
            log.error("No Table[" + tableName + "] Field[" + fieldName + "]");
        }

        return null;
    }

    /**
     * @param tableName
     * @param fieldName
     * @return
     */
    protected Name getFieldNameDesc(final String tableName, final String fieldName) {
        LocalizableContainerIFace container = schemaLocalizer.getContainer(tableName);
        if (container != null) {
            LocalizableItemIFace item = container.getItemByName(fieldName);
            if (item != null) {
                LocalizableStrIFace dn = LocalizerBasePanel.getNameDescForCurrLocale(item);
                if (dn != null) {
                    Name nm = new Name(dn.getText(), dn.getCountry(), dn.getLanguage(), dn.getVariant());
                    return nm;
                }
                log.error("No Name for [" + tableName + "] Field[" + fieldName + "]");
            } else {
                log.error("No Field [" + tableName + "] Field[" + fieldName + "]");
            }
        } else {
            log.error("No Table[" + tableName + "] Field[" + fieldName + "]");
        }

        return null;
    }

    /**
     * Looks for a child node "display" and creates the appropriate object or returns null.
     * @param element the "table".
     * @return null or a Display object
     */
    private Display createDisplay(final Element element) {
        if (element != null) {
            Element fdElement = (Element) element.selectSingleNode("display");
            if (fdElement != null) {
                return new Display(fdElement.attributeValue("view"), fdElement.attributeValue("dataobjformatter"),
                        fdElement.attributeValue("uiformatter"), fdElement.attributeValue("searchdlg"),
                        fdElement.attributeValue("newobjdlg"));
            }
        }
        return null;
    }

    private Vector<FieldAlias> createFieldAliases(final Element element) {
        if (element != null) {
            Vector<FieldAlias> aliases = new Vector<FieldAlias>();
            List<?> items = element.selectNodes("fieldaliases/field");
            if (items.size() > 0) {
                for (Iterator<?> iter = items.iterator(); iter.hasNext();) {
                    Element faItem = (Element) iter.next();
                    FieldAlias fa = new FieldAlias(XMLHelper.getAttr(faItem, "vname", null),
                            XMLHelper.getAttr(faItem, "aname", null));
                    aliases.add(fa);
                }
                return aliases;
            }
        }
        return null;
    }

    /**
     * Given and XML node, returns a Table object by grabbing the appropriate
     * attribute values.
     * 
     * @param element the XML node
     * @return Table object
     */
    private Table createTable(final String className, final String tableName) {
        // get Class Name (or name) from HBM file
        log.info("Processing: " + className);

        // Get Meta Data for HBM
        TableMetaData tableMetaData = tblMetaDataHash.get(className);
        if (tableMetaData == null) {
            // Throw exception if there is an HBM we don't have meta data for
            log.error("Could not retrieve TableMetaData from tblMetaDataHashtable for table: " + className);
            throw new RuntimeException("Could not retrieve TableMetaData from tblMetaDataHashtable for table: "
                    + className + " check to see if table is listed in the file: "
                    + DatamodelHelper.getTableIdFilePath());
        }

        Table tbl = new Table(className, tableName, null, tableMetaData.getId(), tableMetaData.getDisplay(),
                tableMetaData.getFieldAliase(), tableMetaData.isSearchable(), tableMetaData.getBusinessRule(),
                tableMetaData.getAbbrv());
        tbl.setLikeManyToOneHash(tableMetaData.getLikeManyToOneHash());
        return tbl;
    }

    /**
     * @param method
     * @return
     */
    protected String getReturnType(final Method method) {
        Class<?> classObj = method.getReturnType();
        // If there is a better way, PLEASE help me!
        if (classObj == Set.class) {
            ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
            for (Type t : type.getActualTypeArguments()) {
                String cls = t.toString();
                return cls.substring(6, cls.length());
            }
        }
        return classObj.getName();
    }

    /**
     * @param method
     * @return
     */
    protected String getNameFromMethod(final Method method) {
        String name = method.getName();
        name = name.substring(3, 4).toLowerCase() + name.substring(4, name.length());

        return name;
    }

    /**
     * @param method
     * @param type
     * @param joinCol
     * @return
     */
    public Relationship createRelationship(final Method method, final String type,
            final javax.persistence.JoinColumn joinCol, final String otherSideName, final boolean isRequired) {
        Relationship rel = new Relationship(type, getReturnType(method), joinCol != null ? joinCol.name() : "",
                getNameFromMethod(method));
        rel.setOtherSideName(otherSideName);
        rel.setRequired(isRequired);
        return rel;
    }

    /**
     * @param method
     * @param col
     * @return
     */
    public Id createId(final Method method, final javax.persistence.Column col) {
        return new Id(getNameFromMethod(method), getReturnType(method), col.name(), "");
    }

    /**
     * @param method
     * @param col
     * @return
     */
    public Field createField(final Method method, final javax.persistence.Column col, final boolean isLob) {
        String retType;
        String len;

        if (isLob) {
            retType = "text";
            len = retType.equals("java.lang.String") ? Integer.toString(col.length())
                    : (col.length() != 255 ? Integer.toString(col.length()) : "");

        } else {
            retType = isLob ? "text" : getReturnType(method);
            if (retType.equals("int")) {
                retType = "java.lang.Integer";
            }
            len = retType.equals("java.lang.String") ? Integer.toString(col.length())
                    : (col.length() != 255 ? Integer.toString(col.length()) : "");
        }
        Field field = new Field(getNameFromMethod(method), retType, col.name(), len);
        field.setRequired(!col.nullable());
        field.setUpdatable(col.updatable());
        field.setUnique(col.unique());

        return field;
    }

    @SuppressWarnings("unchecked")
    public Class<?> getSetsClassType(final Class<?> cls, final String methodName) {
        try {
            File f = new File(srcCodeDir.getAbsoluteFile() + File.separator + cls.getSimpleName() + ".java");
            if (!f.exists()) {
                log.error("Can't locate source file[" + f.getAbsolutePath() + "]");
                return null;
            }

            List<String> lines = FileUtils.readLines(f);
            for (String line : lines) {
                int sInx = line.indexOf("Set<");
                if (sInx > -1 && line.indexOf(methodName) > -1) {
                    int eInx = line.indexOf(">", sInx);
                    String className = line.substring(sInx + 4, eInx);
                    return Class.forName(packageName + "." + className);
                }
            }

        } catch (ClassNotFoundException ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);

        } catch (IOException ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * @param leftSide
     * @param rightSide
     * @param mappedByName
     * @return
     */
    protected String getRightSideForOneToMany(final Class<?> leftSide, final Class<?> rightSide,
            final String mappedByName) {
        if (StringUtils.isEmpty(mappedByName)) {
            throw new RuntimeException("Couldn't find otherside method name missing for ["
                    + rightSide.getSimpleName() + "]  mappedByName[" + mappedByName + "]");
        }

        for (Method method : rightSide.getMethods()) {
            String methodName = method.getName();
            // Skip if it is a not a getter
            if (!methodName.startsWith("get")) {
                continue;
            }
            //System.out.println("Left Class["+leftSide.getSimpleName()+"]  Right["+rightSide.getSimpleName()+"] Right Side Method ["+methodName+"] Ret["+method.getReturnType().getSimpleName()+"]");

            // Skip if it is a not a ManyToOne
            if (!method.isAnnotationPresent(javax.persistence.ManyToOne.class)) {
                continue;
            }

            Class<?> retType = method.getReturnType();
            boolean isSet = Collection.class.isAssignableFrom(retType);
            if (isSet) {
                Class<?> rt = getSetsClassType(rightSide, methodName);
                if (rt == null) {
                    continue; // probably because of an interface
                }
                retType = rt;
                //System.out.println("Set["+(retType != null ? retType.getSimpleName() : "NULL")+"]");
            }

            // Skip if the Return Types don't match
            if (leftSide != retType) {
                continue;
            }

            return getFieldNameFromMethod(method);

        }
        return null;
    }

    /**
     * @param leftSide
     * @param rightSide
     * @param leftSideVarName
     * @return
     */
    @SuppressWarnings("cast")
    protected String getRightSideForManyToOne(final Class<?> leftSide, final Class<?> rightSide,
            final String leftSideVarName) {
        for (Method method : rightSide.getMethods()) {
            String methodName = method.getName();
            // Skip if it is a not a getter
            if (!methodName.startsWith("get")) {
                continue;
            }
            //System.out.println("getRightSideForManyToOne Left Class["+leftSide.getSimpleName()+"]  Right["+rightSide.getSimpleName()+"] Right Side Method ["+methodName+"] Ret["+method.getReturnType().getSimpleName()+"]");

            // Skip if it is a not a ManyToOne
            if (!method.isAnnotationPresent(javax.persistence.OneToMany.class)) {
                continue;
            }

            Class<?> retType = method.getReturnType();
            boolean isSet = Collection.class.isAssignableFrom(retType);
            if (isSet) {
                Class<?> rt = getSetsClassType(rightSide, methodName);
                if (rt == null) {
                    continue; // probably because of an interface
                }
                retType = rt;
                //System.out.println("Set["+(retType != null ? retType.getSimpleName() : "NULL")+"]");
            }

            if (leftSide != retType) {
                continue;
            }

            javax.persistence.OneToMany oneToMany = (javax.persistence.OneToMany) method
                    .getAnnotation(javax.persistence.OneToMany.class);
            String othersideName = oneToMany.mappedBy();
            if (StringUtils.isNotEmpty(othersideName)) // This should never be null
            {
                //System.out.println("\nXXX["+othersideName+"]["+retType+"]");
                //System.out.println("othersideName["+othersideName+"]  leftSideVarName["+leftSideVarName+"]");
                //System.out.println("["+leftSide.getSimpleName()+"]["+retType.getSimpleName()+"]");
                if (othersideName.equals(leftSideVarName)) {
                    return getFieldNameFromMethod(method);
                }
            }
        }
        return null;
    }

    /**
     * @param leftSide
     * @param rightSide
     * @param leftSideVarName
     * @param isMappedBy
     * @return
     */
    @SuppressWarnings("cast")
    protected String getRightSideForManyToMany(final Class<?> leftSide, final Class<?> rightSide,
            final String leftSideVarName) {
        for (Method method : rightSide.getMethods()) {
            String methodName = method.getName();
            // Skip if it is a not a getter
            if (!methodName.startsWith("get")) {
                continue;
            }
            //System.out.println("getRightSideForManyToMany Left Class["+leftSide.getSimpleName()+"]  Right["+rightSide.getSimpleName()+"] Right Side Method ["+methodName+"] Ret["+method.getReturnType().getSimpleName()+"]");

            // Skip if it is a not a ManyToOne
            if (!method.isAnnotationPresent(javax.persistence.ManyToMany.class)) {
                continue;
            }

            Class<?> retType = method.getReturnType();
            boolean isSet = Collection.class.isAssignableFrom(retType);
            if (isSet) {
                Class<?> rt = getSetsClassType(rightSide, methodName);
                if (rt == null) {
                    continue; // probably because of an interface
                }
                retType = rt;
                //System.out.println("Set["+(retType != null ? retType.getSimpleName() : "NULL")+"]");
            }

            if (leftSide == retType) {
                javax.persistence.ManyToMany manyToMany = (javax.persistence.ManyToMany) method
                        .getAnnotation(javax.persistence.ManyToMany.class);
                // Caller wasn't mappedBy so look for mapped By
                String othersideName = manyToMany.mappedBy();

                if (StringUtils.isNotEmpty(othersideName) && leftSideVarName.equals(othersideName)) {
                    return getFieldNameFromMethod(method);
                }
            }
        }
        return null;
    }

    /**
     * @param leftSide
     * @param rightSide
     * @param mappedByName
     * @return
     */
    @SuppressWarnings("cast")
    protected String getRightSideForOneToOne(final Class<?> leftSide, final Class<?> rightSide,
            final String leftSideVarName, @SuppressWarnings("unused") final String mappedName,
            final boolean isMappedBy) {
        for (Method method : rightSide.getMethods()) {
            String methodName = method.getName();
            // Skip if it is a not a getter
            if (!methodName.startsWith("get")) {
                continue;
            }
            //System.out.println("Left Class["+leftSide.getSimpleName()+"]  Right["+rightSide.getSimpleName()+"] Right Side Method ["+methodName+"] Ret["+method.getReturnType().getSimpleName()+"]");

            // Skip if it is a not a OneToOne
            if (!method.isAnnotationPresent(javax.persistence.OneToOne.class)) {
                continue;
            }

            Class<?> retType = method.getReturnType();
            boolean isSet = Collection.class.isAssignableFrom(retType);
            if (isSet) {
                Class<?> rt = getSetsClassType(rightSide, methodName);
                if (rt == null) {
                    continue; // probably because of an interface
                }
                retType = rt;
                //System.out.println("Set["+(retType != null ? retType.getSimpleName() : "NULL")+"]");
            }

            String othersideName = "";
            if (isMappedBy) {
                othersideName = getFieldNameFromMethod(method);

            } else {
                javax.persistence.OneToOne oneToOne = (javax.persistence.OneToOne) method
                        .getAnnotation(javax.persistence.OneToOne.class);
                // Caller wasn't mappedBy so look for mapped By
                othersideName = oneToOne.mappedBy();
            }

            if (StringUtils.isNotEmpty(othersideName) && leftSideVarName.equals(othersideName)) {
                return getFieldNameFromMethod(method);
            }

        }
        return null;
    }

    /**
     * @param method
     * @return
     */
    protected String getFieldNameFromMethod(final Method method) {
        String methodName = method.getName().substring(3);
        return methodName.substring(0, 1).toLowerCase() + methodName.substring(1, methodName.length());
    }

    /**
     * @param className
     * @param tableList
     */
    @SuppressWarnings("cast")
    protected void processClass(final String className, final List<Table> tableList) {
        try {
            Class<?> classObj = Class.forName(packageName + "." + className);

            Table table = null;
            String tableName = null;

            if (classObj.isAnnotationPresent(javax.persistence.Table.class)) {
                Vector<TableIndex> indexes = new Vector<TableIndex>();

                javax.persistence.Table tableAnno = (javax.persistence.Table) classObj
                        .getAnnotation(javax.persistence.Table.class);
                tableName = tableAnno.name();

                org.hibernate.annotations.Table hiberTableAnno = (org.hibernate.annotations.Table) classObj
                        .getAnnotation(org.hibernate.annotations.Table.class);
                if (hiberTableAnno != null) {
                    //System.out.println("Table Indexes: ");
                    for (Index index : hiberTableAnno.indexes()) {
                        //System.out.println("  "+index.name() + "  "+ index.columnNames());
                        indexes.add(new TableIndex(index.name(), index.columnNames()));
                    }
                }

                table = createTable(packageName + "." + className, tableName);
                if (includeDesc) {
                    table.setDesc(getTableDesc(tableName));
                    table.setNameDesc(getTableNameDesc(tableName));
                }
                table.setIndexes(indexes);
                tableList.add(table);
            }

            if (table != null) {
                boolean isLob = false;
                for (Method method : classObj.getMethods()) {
                    String methodName = method.getName();
                    if (!methodName.startsWith("get")) {
                        continue;
                    }

                    if (DEBUG) {
                        System.out.println(className + " " + method.getName());
                    }

                    Type type = method.getGenericReturnType();
                    Class<?> typeClass;
                    if (type instanceof Class<?>) {
                        typeClass = (Class<?>) type;

                    } else if (type instanceof ParameterizedType) {
                        typeClass = null;
                        for (Type t : ((ParameterizedType) type).getActualTypeArguments()) {
                            if (t instanceof Class<?>) {
                                typeClass = (Class<?>) t;
                            }
                        }
                    } else {
                        if (!method.getName().equals("getDataObj") && !method.getName().equals("getTreeRootNode")) {
                            log.warn("Not handled: " + type);
                        }
                        typeClass = null;
                    }

                    // rods 07/10/08 - Used to skip all relationships that point to themselves
                    // that works now and is needed.
                    if (typeClass == null || typeClass == AttributeIFace.class
                            || typeClass == PickListItemIFace.class || typeClass == RecordSetItemIFace.class) {
                        continue;
                    }

                    String thisSideName = getFieldNameFromMethod(method);

                    if (method.isAnnotationPresent(javax.persistence.Lob.class)) {
                        isLob = true;
                    }

                    if (method.isAnnotationPresent(javax.persistence.Column.class)) {
                        if (method.isAnnotationPresent(javax.persistence.Id.class)) {
                            table.addId(createId(method, (javax.persistence.Column) method
                                    .getAnnotation(javax.persistence.Column.class)));
                        } else {
                            Field field = createField(method,
                                    (javax.persistence.Column) method.getAnnotation(javax.persistence.Column.class),
                                    isLob);
                            if (includeDesc) {
                                field.setDesc(getFieldDesc(tableName, field.getName()));
                                field.setNameDesc(getFieldNameDesc(tableName, field.getName()));
                            }

                            if (typeClass == java.util.Calendar.class) {
                                String mName = method.getName() + "Precision";
                                for (Method mthd : classObj.getMethods()) {
                                    if (mthd.getName().equals(mName)) {
                                        field.setPartialDate(true);
                                        field.setDatePrecisionName(field.getName() + "Precision");
                                        break;
                                    }
                                }
                            }
                            table.addField(field);
                        }

                    } else if (method.isAnnotationPresent(javax.persistence.ManyToOne.class)) {
                        javax.persistence.ManyToOne oneToMany = (javax.persistence.ManyToOne) method
                                .getAnnotation(javax.persistence.ManyToOne.class);

                        boolean isSave = false;
                        for (CascadeType ct : oneToMany.cascade()) {
                            if (ct == CascadeType.ALL || ct == CascadeType.PERSIST) {
                                isSave = true;
                            }
                        }
                        isSave = !isSave ? isOKToSave(method) : isSave;

                        String otherSideName = getRightSideForManyToOne(classObj, typeClass, thisSideName);

                        javax.persistence.JoinColumn join = method
                                .isAnnotationPresent(javax.persistence.JoinColumn.class)
                                        ? (javax.persistence.JoinColumn) method
                                                .getAnnotation(javax.persistence.JoinColumn.class)
                                        : null;
                        if (join != null) {
                            //String othersideName = typeClass == null ? "" : getOthersideName(classObj, typeClass, thisSideName, RelType.OneToMany);
                            Relationship rel = createRelationship(method, "many-to-one", join, otherSideName,
                                    join != null ? !join.nullable() : false);
                            table.addRelationship(rel);
                            rel.setSave(isSave);

                            if (includeDesc) {
                                rel.setDesc(getFieldDesc(tableName, rel.getRelationshipName()));
                                rel.setNameDesc(getFieldNameDesc(tableName, rel.getRelationshipName()));
                            }

                        } else {
                            log.error("No Join!");
                        }

                    } else if (method.isAnnotationPresent(javax.persistence.ManyToMany.class)) {
                        javax.persistence.ManyToMany manyToMany = method
                                .getAnnotation(javax.persistence.ManyToMany.class);

                        String othersideName = manyToMany.mappedBy();
                        if (StringUtils.isEmpty(othersideName)) {
                            othersideName = getRightSideForManyToMany(classObj, typeClass,
                                    getFieldNameFromMethod(method));
                        }

                        boolean isSave = false;
                        for (CascadeType ct : manyToMany.cascade()) {
                            if (ct == CascadeType.ALL || ct == CascadeType.PERSIST) {
                                isSave = true;
                            }
                        }
                        isSave = !isSave ? isOKToSave(method) : isSave;

                        javax.persistence.JoinColumn join = method
                                .isAnnotationPresent(javax.persistence.JoinColumn.class)
                                        ? (javax.persistence.JoinColumn) method
                                                .getAnnotation(javax.persistence.JoinColumn.class)
                                        : null;
                        Relationship rel = createRelationship(method, "many-to-many", join, othersideName,
                                join != null ? !join.nullable() : false);
                        rel.setLikeManyToOne(table.getIsLikeManyToOne(rel.getRelationshipName()));
                        rel.setSave(isSave);

                        table.addRelationship(rel);
                        if (includeDesc) {
                            rel.setDesc(getFieldDesc(tableName, rel.getRelationshipName()));
                            rel.setNameDesc(getFieldNameDesc(tableName, rel.getRelationshipName()));
                        }

                        javax.persistence.JoinTable joinTable = method
                                .getAnnotation(javax.persistence.JoinTable.class);
                        if (joinTable != null) {
                            rel.setJoinTableName(joinTable.name());
                        }

                    } else if (method.isAnnotationPresent(javax.persistence.OneToMany.class)) {
                        javax.persistence.OneToMany oneToMany = (javax.persistence.OneToMany) method
                                .getAnnotation(javax.persistence.OneToMany.class);

                        String othersideName = oneToMany.mappedBy();
                        if (StringUtils.isEmpty(othersideName)) {
                            // This Should never happen
                            othersideName = getRightSideForOneToMany(classObj, typeClass, oneToMany.mappedBy());
                        }

                        boolean isSave = false;
                        for (CascadeType ct : oneToMany.cascade()) {
                            if (ct == CascadeType.ALL || ct == CascadeType.PERSIST) {
                                isSave = true;
                            }
                        }
                        isSave = !isSave ? isOKToSave(method) : isSave;

                        javax.persistence.JoinColumn join = method
                                .isAnnotationPresent(javax.persistence.JoinColumn.class)
                                        ? (javax.persistence.JoinColumn) method
                                                .getAnnotation(javax.persistence.JoinColumn.class)
                                        : null;
                        Relationship rel = createRelationship(method, "one-to-many", join, othersideName,
                                join != null ? !join.nullable() : false);
                        rel.setLikeManyToOne(table.getIsLikeManyToOne(rel.getRelationshipName()));
                        rel.setSave(isSave);
                        table.addRelationship(rel);
                        if (includeDesc) {
                            rel.setDesc(getFieldDesc(tableName, rel.getRelationshipName()));
                            rel.setNameDesc(getFieldNameDesc(tableName, rel.getRelationshipName()));
                        }

                    } else if (method.isAnnotationPresent(javax.persistence.OneToOne.class)) {
                        javax.persistence.OneToOne oneToOne = (javax.persistence.OneToOne) method
                                .getAnnotation(javax.persistence.OneToOne.class);
                        String leftSideVarName = getFieldNameFromMethod(method);
                        boolean isMappedBy = true;
                        String othersideName = oneToOne.mappedBy();
                        if (StringUtils.isEmpty(othersideName)) {
                            isMappedBy = false;
                            othersideName = getRightSideForOneToOne(classObj, typeClass, leftSideVarName,
                                    othersideName, isMappedBy);
                        }

                        boolean isSave = false;
                        for (CascadeType ct : oneToOne.cascade()) {
                            if (ct == CascadeType.ALL || ct == CascadeType.PERSIST) {
                                isSave = true;
                            }
                        }
                        isSave = !isSave ? isOKToSave(method) : isSave;

                        javax.persistence.JoinColumn join = method
                                .isAnnotationPresent(javax.persistence.JoinColumn.class)
                                        ? (javax.persistence.JoinColumn) method
                                                .getAnnotation(javax.persistence.JoinColumn.class)
                                        : null;
                        Relationship rel = createRelationship(method, "one-to-one", join, othersideName,
                                join != null ? !join.nullable() : false);
                        rel.setSave(isSave);
                        table.addRelationship(rel);
                        if (includeDesc) {
                            rel.setDesc(getFieldDesc(tableName, rel.getRelationshipName()));
                            rel.setNameDesc(getFieldNameDesc(tableName, rel.getRelationshipName()));
                        }

                    }
                    isLob = false;
                }

                // This updates each field as to whether it is an index
                table.updateIndexFields();
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * @param method
     * @return
     */
    @SuppressWarnings("cast")
    protected boolean isOKToSave(final Method method) {
        org.hibernate.annotations.Cascade hibCascade = (org.hibernate.annotations.Cascade) method
                .getAnnotation(org.hibernate.annotations.Cascade.class);
        if (hibCascade != null) {
            for (org.hibernate.annotations.CascadeType ct : hibCascade.value()) {
                if (ct == org.hibernate.annotations.CascadeType.ALL
                        || ct == org.hibernate.annotations.CascadeType.PERSIST
                        || ct == org.hibernate.annotations.CascadeType.SAVE_UPDATE) {
                    return true;
                }
            }
        }
        return false;
    }

    @SuppressWarnings("unchecked")
    protected void addCascadeRule(Class<?> cls, Method method, final String rule) {

        String import1 = "import org.hibernate.annotations.Cascade;";
        //String import2 = "import org.hibernate.annotations.CascadeType;";
        try {
            File f = new File(srcCodeDir.getAbsoluteFile() + File.separator + cls.getSimpleName() + ".java");
            if (!f.exists()) {
                log.error("Can't locate source file[" + f.getAbsolutePath() + "]");
                return;
            }

            List<String> strLines = FileUtils.readLines(f);
            Vector<String> lines = new Vector<String>();
            //boolean addedImports = false;
            boolean fnd = false;
            boolean passedImport = false;

            String methodName = method.getName() + "(";
            for (String line : strLines) {
                if (!passedImport && line.indexOf("import") > -1) {
                    passedImport = true;
                }

                if (!fnd && line.indexOf(import1) > -1) {
                    fnd = true;
                }

                /*if (!fnd && !addedImports && passedImport && (line.indexOf("/**") > -1 || line.indexOf("@Entity") > -1))
                {
                lines.add(import1);
                lines.add(import2);
                lines.add("");
                addedImports = true;
                }*/

                if (line.indexOf(methodName) > -1 && line.indexOf("public") > -1) {
                    lines.add(rule);
                }
                lines.add(line);
            }

            FileUtils.writeLines(f, lines);

        } catch (IOException ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
        }

    }

    /**
     * @param className
     * @param tableList
     */
    @SuppressWarnings("unchecked")
    protected void processCascadeAddCascade(final String className, final List<Table> tableList) {
        //System.out.println(className);
        try {
            Class<?> classObj = Class.forName(packageName + "." + className);

            Table table = null;
            String tableName = null;

            if (classObj.isAnnotationPresent(javax.persistence.Table.class)) {
                Vector<TableIndex> indexes = new Vector<TableIndex>();

                javax.persistence.Table tableAnno = (javax.persistence.Table) classObj
                        .getAnnotation(javax.persistence.Table.class);
                tableName = tableAnno.name();

                org.hibernate.annotations.Table hiberTableAnno = (org.hibernate.annotations.Table) classObj
                        .getAnnotation(org.hibernate.annotations.Table.class);
                if (hiberTableAnno != null) {
                    for (Index index : hiberTableAnno.indexes()) {
                        indexes.add(new TableIndex(index.name(), index.columnNames()));
                    }
                }

                table = createTable(packageName + "." + className, tableName);
                table.setIndexes(indexes);
                tableList.add(table);
            }

            if (table != null) {

                for (Method method : classObj.getMethods()) {
                    String methodName = method.getName();
                    if (!methodName.startsWith("get")
                            || method.isAnnotationPresent(javax.persistence.Transient.class)) {
                        continue;
                    }

                    //String thisSideName = getFieldNameFromMethod(method);

                    if (method.isAnnotationPresent(javax.persistence.ManyToOne.class)) {
                        if (!method.isAnnotationPresent(org.hibernate.annotations.Cascade.class)) {
                            System.out.println("Missing Cascade[" + method.getName() + "]");
                            missing++;
                            //addCascadeRule(classObj, method, "    @org.hibernate.annotations.Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.LOCK })");
                        }

                    } else if (method.isAnnotationPresent(javax.persistence.ManyToMany.class)) {
                        if (!method.isAnnotationPresent(org.hibernate.annotations.Cascade.class)) {
                            System.out.println("Missing Cascade[" + method.getName() + "]");
                            missing++;
                            //addCascadeRule(classObj, method, "    @org.hibernate.annotations.Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.LOCK })");
                        }

                    } else if (method.isAnnotationPresent(javax.persistence.OneToMany.class)) {
                        if (!method.isAnnotationPresent(org.hibernate.annotations.Cascade.class)) {
                            System.out.println("Missing Cascade[" + method.getName() + "]");
                            missing++;
                            addCascadeRule(classObj, method,
                                    "    @org.hibernate.annotations.Cascade( { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })");

                        }

                    } else if (method.isAnnotationPresent(javax.persistence.OneToOne.class)) {
                        if (!method.isAnnotationPresent(org.hibernate.annotations.Cascade.class)) {
                            //System.out.println("Missing Cascade["+method.getName()+"]");
                            missing++;
                        }

                    }
                }

                // This updates each field as to whether it is an index
                table.updateIndexFields();
            }

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    protected void removeCascadeRule(Class<?> cls, Method method) {

        try {
            File f = new File(srcCodeDir.getAbsoluteFile() + File.separator + cls.getSimpleName() + ".java");
            if (!f.exists()) {
                log.error("Can't locate source file[" + f.getAbsolutePath() + "]");
                return;
            }

            List<String> strLines = FileUtils.readLines(f);
            Vector<String> lines = new Vector<String>();

            String methodName = method.getName() + "(";
            int inx = 0;
            for (String line : strLines) {

                if (line.indexOf(methodName) > -1 && line.indexOf("public") > -1) {
                    int i = inx;
                    int stop = i - 10;
                    System.out.println("[" + strLines.get(i) + "]");
                    while (!StringUtils.contains(strLines.get(i), "@Cascade") && i > stop) {
                        i--;
                        System.out.println("[" + strLines.get(i) + "]");
                    }

                    if (i < stop || StringUtils.contains(strLines.get(i), "@Cascade")) {
                        lines.remove(i);
                    }
                }
                lines.add(line);
                inx++;
            }

            FileUtils.writeLines(f, lines);

        } catch (IOException ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
        }
    }

    /**
     * @param className
     * @param tableList
     */
    @SuppressWarnings("unchecked")
    protected void processCascade(final String className, final List<Table> tableList) {
        //System.out.println(className);
        try {
            Class<?> classObj = Class.forName(packageName + "." + className);

            //Table   table       = null; 
            //String  tableName   = null;

            if (classObj.isAnnotationPresent(javax.persistence.Table.class)) {
                for (Method method : classObj.getMethods()) {
                    String methodName = method.getName();
                    if (!methodName.startsWith("get")
                            || method.isAnnotationPresent(javax.persistence.Transient.class)) {
                        continue;
                    }

                    if (method.isAnnotationPresent(javax.persistence.ManyToOne.class)) {
                        if (method.isAnnotationPresent(org.hibernate.annotations.Cascade.class)) {
                            System.out.println("Missing Cascade[" + method.getName() + "]");
                            missing++;
                            removeCascadeRule(classObj, method);
                        }
                    }
                }
            }

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
        }
    }

    /**
     * Reads in hbm files and generates datamodel tree.
     * @return List datamodel tree
     */
    @SuppressWarnings({ "unchecked", "cast" })
    public List<Table> generateDatamodelTree(final List<Table> tableList, final String dataModelPath) {
        try {
            log.debug("Preparing to read in DataModel Classes files from  path: " + dataModelPath);

            srcCodeDir = new File(dataModelPath);

            String path = srcCodeDir.getAbsolutePath();
            log.info(path);
            //dir = new File(path.substring(0, path.lastIndexOf(File.separator)));

            // This filter only returns directories
            FileFilter fileFilter = new FileFilter() {
                public boolean accept(File file) {
                    return file.toString().indexOf(".java") != -1;
                }
            };

            String PACKAGE = "package ";
            String CLASS = "public class ";

            File[] files = srcCodeDir.listFiles(fileFilter);
            int count = 0;
            for (File file : files) {
                if (showDebug)
                    log.debug("Reading    " + file.getAbsolutePath());
                List<?> lines = FileUtils.readLines(file);
                count++;
                if (showDebug)
                    log.debug("Processing " + count + " of " + files.length + "  " + file.getAbsolutePath());

                String className = null;

                for (Object lineObj : lines) {
                    String line = ((String) lineObj).trim();
                    //System.out.println(line);
                    if (line.startsWith(PACKAGE)) {
                        packageName = line.substring(PACKAGE.length(), line.length() - 1);
                    }
                    if (StringUtils.contains(line, CLASS)) {
                        String str = line.substring(CLASS.length());
                        while (str.charAt(0) == ' ') {
                            str = str.substring(1);
                        }

                        int eInx = str.indexOf(' ');
                        if (eInx == -1) {
                            className = str;
                        } else {
                            className = str.substring(0, eInx);
                        }
                        break;
                    }
                }

                if (className != null) {
                    if (!StringUtils.contains(className, "SpUI")) {
                        processClass(className, tableList);
                    }

                    // These were used for correcting Cascading rules
                    // Eventually these can be removed along with the methods.
                    //processCascade(className, tableList);
                    //processCascadeAddCascade(className, tableList);
                } else {
                    String fileName = file.getName();
                    if (!StringUtils.contains(fileName, "DataModelObjBase")
                            && !StringUtils.contains(fileName, "CollectionMember")
                            && !StringUtils.contains(fileName, "DisciplineMember")
                            && !StringUtils.contains(fileName, "UserGroupScope")
                            && !StringUtils.contains(fileName, "Treeable")
                            && !StringUtils.contains(fileName, "SpLocaleBase")
                            && !StringUtils.contains(fileName.toLowerCase(), "iface")
                            && !StringUtils.contains(fileName, "BaseTreeDef")
                            && !StringUtils.contains(fileName, "TreeDefItemStandardEntry")) {
                        throw new RuntimeException("Couldn't locate class name for " + file.getAbsolutePath());
                    }
                }
            }
            System.out.println(missing);
            return tableList;

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
            log.fatal(ex);
        }
        return null;
    }

    /**
     * Gets column name.
     * @param element the element
     * @return the name of the column
     */
    public String getColumnName(final Element element) {
        String columnName = null;
        for (Iterator<?> i2 = element.elementIterator("column"); i2.hasNext();) {
            Element element1 = (Element) i2.next();
            columnName = element1.attributeValue("name");
        }
        return columnName;
    }

    protected void setRelToZeroToOne(Collection<Relationship> rels, final String name) {
        for (Relationship rel : rels) {
            if (rel.getRelationshipName().equalsIgnoreCase(name)) {
                rel.setType("zero-to-one");
            }
        }
    }

    protected void adjustRelsForZeroToOne(final Table tbl) {
        String shortTableName = StringUtils.substringAfterLast(tbl.getName(), ".");
        if (shortTableName.equals("Locality")) {
            setRelToZeroToOne(tbl.getRelationships(), "localityDetails");
            setRelToZeroToOne(tbl.getRelationships(), "geoCoordDetails");
        }
    }

    /**
     * Takes a list and prints out data model file using betwixt.
     * @param classesList the class list
     */
    public boolean writeTree(final List<Table> classesList) {
        if (doRelsToZeroToOne) {
            for (Table tbl : classesList) {
                adjustRelsForZeroToOne(tbl);
            }
        }

        try {
            if (classesList == null) {
                log.error("Datamodel information is null - datamodel file will not be written!!");
                return false;
            }
            log.info("writing data model tree to file: " + DatamodelHelper.getDatamodelFilePath());

            File file = DatamodelHelper.getDatamodelFilePath();
            FileWriter fw = new FileWriter(file);
            fw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            fw.write("<!-- \n");
            fw.write("    Do Not Edit this file!\n");
            fw.write("    Run DatamodelGenerator \n");
            Date date = new Date();
            fw.write("    Generated: " + date.toString() + "\n");
            fw.write("-->\n");

            //using betwixt for writing out datamodel file.  associated .betwixt files allow you to map and define 
            //output format of attributes in xml file.
            BeanWriter beanWriter = new BeanWriter(fw);
            XMLIntrospector introspector = beanWriter.getXMLIntrospector();

            introspector.getConfiguration().setWrapCollectionsInElement(false);

            beanWriter.getBindingConfiguration().setMapIDs(false);
            beanWriter.setWriteEmptyElements(false);
            beanWriter.enablePrettyPrint();
            beanWriter.write("database", classesList);

            fw.close();

            return true;

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            log.error("error writing writeTree", ex);
            return false;
        }
    }

    /**
     * Reads in file that provides listing of tables with their respective Id's and default views.
     * @return boolean true if reading of tableId file was successful.
     */
    private boolean readTableMetadataFromFile(final String tableIdListingFilePath) {
        Hashtable<String, Boolean> abbrvHashLocal = new Hashtable<String, Boolean>();

        log.info("Preparing to read in Table and TableID listing from file: " + tableIdListingFilePath);
        try {
            File tableIdFile = new File(tableIdListingFilePath);
            FileInputStream fileInputStream = new FileInputStream(tableIdFile);
            SAXReader reader = new SAXReader();
            reader.setValidation(false);
            org.dom4j.Document doc = reader.read(fileInputStream);
            Element root = doc.getRootElement();
            Element dbNode = (Element) root.selectSingleNode("database");
            if (dbNode != null) {
                for (Iterator<?> i = dbNode.elementIterator("table"); i.hasNext();) {
                    Element element = (Element) i.next();
                    String tablename = element.attributeValue("name");
                    String defaultView = element.attributeValue("view");
                    String id = element.attributeValue("id");
                    String abbrv = XMLHelper.getAttr(element, "abbrev", null);
                    boolean isSearchable = XMLHelper.getAttr(element, "searchable", false);

                    if (StringUtils.isNotEmpty(abbrv)) {
                        if (abbrvHashLocal.get(abbrv) == null) {
                            abbrvHashLocal.put(abbrv, true);
                        } else {
                            throw new RuntimeException(
                                    "`abbrev` [" + abbrv + "]  or table[" + tablename + "] ids already in use.");
                        }

                    } else {
                        throw new RuntimeException("`abbrev` is missing or empty for table[" + tablename + "]");
                    }

                    String busRule = "";
                    Element brElement = (Element) element.selectSingleNode("businessrule");
                    if (brElement != null) {
                        busRule = brElement.getTextTrim();
                    }
                    //log.debug("Creating TableMetaData and putting in tblMetaDataHashtable for name: " + tablename + " id: " + id + " defaultview: " + defaultView);

                    TableMetaData tblMetaData = new TableMetaData(id, defaultView, createDisplay(element),
                            createFieldAliases(element), isSearchable, busRule, abbrv);
                    tblMetaDataHash.put(tablename, tblMetaData);

                    for (Iterator<?> ir = element.elementIterator("relationship"); ir.hasNext();) {
                        Element relElement = (Element) ir.next();
                        String relName = relElement.attributeValue("relationshipname");
                        boolean isLike = XMLHelper.getAttr(relElement, "likemanytoone", false);
                        tblMetaData.setIsLikeManyToOne(relName, isLike);
                    }

                }

            } else {
                log.debug("Ill-formatted file for reading in Table and TableID listing.  Filename:"
                        + tableIdFile.getAbsolutePath());
            }
            fileInputStream.close();
            return true;

        } catch (Exception ex) {
            edu.ku.brc.af.core.UsageTracker.incrHandledUsageCount();
            edu.ku.brc.exceptions.ExceptionTracker.getInstance().capture(DatamodelGenerator.class, ex);
            ex.printStackTrace();
            log.fatal(ex);
        }
        return false;

    }

    /**
     * @param includeDescArg
     */
    public void process(final String outputFileName) {
        if (StringUtils.isNotEmpty(outputFileName)) {
            DatamodelHelper.setOutputFileName(outputFileName);
        }

        System.out.println("Starting...");
        List<Table> tableList = new ArrayList<Table>(150);
        String tableIdListingFilePath = DatamodelHelper.getTableIdFilePath();

        if (readTableMetadataFromFile(tableIdListingFilePath)) {
            String dmSrc = DatamodelHelper.getDataModelSrcDirPath();
            tableList = generateDatamodelTree(tableList, dmSrc);

            // Sort all the elements by class name
            Collections.sort(tableList);

            boolean didWrite = writeTree(tableList);
            if (!didWrite) {
                log.error("Failed to write out datamodel document");
            }
        } else {
            log.error("Could not find table/ID listing file for input ");
        }
        log.info("Done.");
        System.out.println("Done.");
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        DatamodelGenerator datamodelWriter = new DatamodelGenerator(false);
        datamodelWriter.process(null);
    }

}