Converting line delimiters back to Unix style. Somehow this file was
switched from unix to win delimiters on a previous commit.
diff --git a/jpa/plugins/org.eclipse.jpt.jpa.gen/src/org/eclipse/jpt/jpa/gen/internal/ORMGenCustomizer.java b/jpa/plugins/org.eclipse.jpt.jpa.gen/src/org/eclipse/jpt/jpa/gen/internal/ORMGenCustomizer.java
index f4e5d17..b053bf5 100644
--- a/jpa/plugins/org.eclipse.jpt.jpa.gen/src/org/eclipse/jpt/jpa/gen/internal/ORMGenCustomizer.java
+++ b/jpa/plugins/org.eclipse.jpt.jpa.gen/src/org/eclipse/jpt/jpa/gen/internal/ORMGenCustomizer.java
@@ -1,895 +1,895 @@
-/*******************************************************************************
- * Copyright (c) 2007, 2012 Oracle. All rights reserved.
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0, which accompanies this distribution
- * and is available at http://www.eclipse.org/legal/epl-v10.html.
- *
- * Contributors:
- * Oracle - initial API and implementation
- ******************************************************************************/
-package org.eclipse.jpt.jpa.gen.internal;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.eclipse.jpt.common.utility.internal.StringTools;
-import org.eclipse.jpt.jpa.db.Column;
-import org.eclipse.jpt.jpa.db.Schema;
-import org.eclipse.jpt.jpa.db.Table;
-import org.eclipse.jpt.jpa.gen.internal.plugin.JptJpaGenPlugin;
-import org.eclipse.jpt.jpa.gen.internal.util.DTPUtil;
-import org.eclipse.jpt.jpa.gen.internal.util.FileUtil;
-import org.eclipse.jpt.jpa.gen.internal.util.ForeignKeyInfo;
-import org.eclipse.jpt.jpa.gen.internal.util.StringUtil;
-
-/**
- * Contains the information used to customize the database schema to ORM entity
- * generation.
- *
- * <p>The customization settings are mainly exposed in the form of
- * properties. There are no assumptions in this class about the meaning of the
- * property names. Properties can be associated to specific tables and table
- * columns, or globally for any table and/or column.
- *
- * <p>Subclass can implement the sets of abstract methods to provide ORM vendor
- * specific properties.
- *
- */
-public abstract class ORMGenCustomizer implements java.io.Serializable
-{
- private final static long serialVersionUID = 1;
-
- /**
- * A value passed for the table name argument to get/setProperty
- * indicating that the value applies to any table.
- */
- public static final String ANY_TABLE = "__anyTable__";
- public static final String GENERATE_DDL_ANNOTATION = "generateDDLAnnotations";
- /*the string used in the property name in mProps to indicate
- * a null table value.*/
- private static final String NULL_TABLE = "";
- /*the string used in the property name in mProps to indicate
- * a null column value.*/
- private static final String NULL_COLUMN = "";
-
- /*This version number is written in the header of the customization stream
- * and read at de-serialization time, if it is different then the file is invalidated.
- */
- private static final int FILE_VERSION = 2;
-
- private static final String UPDATE_CONFIG_FILE = "updateConfigFile";
-
- private transient Schema mSchema;
- private transient File mFile;
-
- private List<String> mTableNames;
- /*key: table name, value: ORMGenTable object.*/
- private transient Map<String , ORMGenTable> mTables;
- /*the <code>Association</code> objects sorted by their "from"
- * table name. Includes all association derived from foreign keys
- * in user selected tables. Since some of the foreign keys may point to table
- * user does not select, this list may be different from mValidAssociations
- */
- private List<Association> mAssociations;
- /*
- * List of valid associations within the user selected tables
- */
- private transient List<Association> mValidAssociations;
- private transient boolean mInvalidForeignAssociations;
-
- /*the property name is in the form $tableName.$columnName.$propertyName.
- * Where tableName could be NULL_TABLE or ANY_TABLE
- * and columnName could be NULL_COLUMN*/
- private Map<String, String> mProps = new java.util.HashMap<String, String>();
-
- private transient DatabaseAnnotationNameBuilder databaseAnnotationNameBuilder = DatabaseAnnotationNameBuilder.Default.INSTANCE;
-
- private boolean mUpdatePersistenceXml = true;
-
- //EclipseLink mapping file default name
- private static final String DEFAULT_XML_MAPPING_FILE = "META-INF/eclipselink-orm.xml";
- private static final String XML_MAPPING_FILE = "xmlMappingFileName";
-
- private static final String PLATFORM_VERSION_PROPERTY = "platformVersion";
-
- //-----------------------------------------
- //---- abstract methods
- //-----------------------------------------
- /**
- * Returns all the primary key generator schemes.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract List<String> getAllIdGenerators();
- /**
- * Returns the string representing the developer-assigned id generator.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String getNoIdGenerator();
- /**
- * Returns the string representing the identity id generator.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String getIdentityIdGenerator();
- /**
- * Returns the strings representing the sequence generators.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract Set<String> getSequenceIdGenerators();
- /**
- * Returns a property type from the given database column.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String getPropertyTypeFromColumn(Column column) ;
- /**
- * Returns all the strings representing property types.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String[] getAllPropertyTypes();
- /**
- * Returns all the strings representing property mapping kinds.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String[] getAllMappingKinds();
- /**
- * Returns the basic (default) property mapping kind.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String getBasicMappingKind();
- /**
- * Returns the id (primary key) property mapping kind.
- * This can return any strings as far as the Velocity template
- * processor understand them.
- */
- public abstract String getIdMappingKind();
- /**
- * Interacts with the user to edit the cascade of the given
- * role.
- * This method should also call <code>AssociationRole.setCascade</code>.
- *
- * @return false if the user interaction is cancelled.
- */
- public abstract boolean editCascade(AssociationRole role);
-
- //-----------------------------------------
- //-----------------------------------------
-
- /**
- * @param file The file that contains the customization settings.
- * The file is created if necessary when the <code>save</code>
- * method is called.
- */
- public void init( File file, Schema schema) {
- this.mSchema = schema;
- mFile = file;
-
- if (!file.exists()) {
- setProperty(ORMGenTable.DEFAULT_FETCH, ORMGenTable.DEFAULT_FETCH, ORMGenCustomizer.ANY_TABLE, null);
- return;
- }
- InputStream istream = null;
- ObjectInputStream ois = null;
- try
- {
- //read it in a file first to speedup deserialization
- byte[] bytes = FileUtil.readFile(file);
- istream = new ByteArrayInputStream(bytes);
- ois = new ObjectInputStream(istream);
-
- FileHeader header = (FileHeader)ois.readObject();
- if (header.mVersion == FILE_VERSION) {
- ORMGenCustomizer customizer = (ORMGenCustomizer)ois.readObject();
- restore(customizer);
- }
- } catch (Exception ex) {
- JptJpaGenPlugin.instance().logError(ex, "***ORMGenCustomizer.load failed {0}", file); //$NON-NLS-1$
- }
- finally
- {
- if (ois != null)
- {
- try
- {
- ois.close();
- } catch (IOException e) {
- }
- }
-
- if (istream != null)
- {
- try
- {
- istream.close();
- } catch (IOException e) {
- }
- }
- }
- }
-
- public File getFile(){
- return this.mFile;
- }
-
- public void setSchema(Schema schema){
- this.mSchema = schema;
- }
-
- public Schema getSchema(){
- return mSchema;
- }
-
- /**
- * Empty constructor needed by the deserialization.
- */
- public ORMGenCustomizer() {
- super();
- }
-
- /**
- * Saves the customization file.
- * The file is created if necessary.
- */
- public void save() throws IOException {
- //System.out.println("---ORMGenCustomizer.save: " + mFile);
- if (!mFile.exists() && !mFile.createNewFile()) {
- return;
- }
- java.io.FileOutputStream fos = null;
- java.io.ObjectOutputStream oos = null;
- boolean deleteIt = true;
- try {
- fos = new java.io.FileOutputStream(mFile);
- oos = new java.io.ObjectOutputStream(fos);
- FileHeader header = new FileHeader();
- oos.writeObject(header);
- oos.writeObject(this);
- deleteIt = false;
- } catch (Exception ex) {
- //deleteIt is true, so the cache is not saved.
- JptJpaGenPlugin.instance().logError(ex, "Unable to save the ORMGenCustomizer file: {0}", mFile); //$NON-NLS-1$
- } finally {
- try {
- if (oos!=null) oos.close();
- if (fos!=null) fos.close();
- if (deleteIt) {
- mFile.delete();
- }
- } catch (java.io.IOException ex2) {}
- }
- }
-
- public DatabaseAnnotationNameBuilder getDatabaseAnnotationNameBuilder() {
- return this.databaseAnnotationNameBuilder;
- }
- public void setDatabaseAnnotationNameBuilder(DatabaseAnnotationNameBuilder databaseAnnotationNameBuilder) {
- if (databaseAnnotationNameBuilder == null) {
- throw new NullPointerException("database annotation name builder is required"); //$NON-NLS-1$
- }
- this.databaseAnnotationNameBuilder = databaseAnnotationNameBuilder;
- }
-
- /**
- * Returns {@link #GENERATE_DDL_ANNOTATION} indicating whether
- * the optional DDL parameters like length, nullable, unqiue, etc should be generated
- * in @Column annotation.
- * defaults to false.
- */
- public boolean isGenerateDDLAnnotations() {
- return "true".equals(getProperty(GENERATE_DDL_ANNOTATION, ANY_TABLE, null)); //defaults to false
- }
-
- /**
- * Returns a property value.
- */
- public String getProperty(String propertyName, String tableName, String colName) {
- String key = getPropKey(propertyName, tableName, colName);
- String value = mProps.get(key);
- /*if the key does not exist and it is a table property then
- * get the default table property.*/
- if (value == null && tableName != null && colName == null && !tableName.equals(ANY_TABLE)) {
- value = getProperty(propertyName, ANY_TABLE, colName);
- }
- return value;
- }
- /**
- * Changes a property value.
- *
- * @param value The new value, could be null.
- */
- public void setProperty(String propertyName, String value, String tableName, String colName) {
- String key = getPropKey(propertyName, tableName, colName);
- if (value != null) {
- mProps.put(key, value);
- } else {
- mProps.remove(key);
- }
- }
- /**
- * Same as {@link #getProperty(String, String, String)} but
- * converts the value to boolean.
- */
- public boolean getBooleanProperty(String propertyName, String tableName, String colName) {
- String value = getProperty(propertyName, tableName, colName);
- return "true".equals(value);
- }
- /**
- * Changes a table boolean property value.
- */
- public void setBooleanProperty(String propertyName, boolean value, String tableName, String colName) {
- setProperty(propertyName, value ? "true" : "false", tableName, colName);
- }
- /**
- * Returns the names of the tables to generate.
- */
- @SuppressWarnings("unchecked")
- public List<String> getTableNames() {
- return mTableNames != null ? mTableNames : java.util.Collections.EMPTY_LIST;
- }
-
- /**
- * Returns the fetch type annotation member value, or empty string
- * if none.
- * Empty string is returned instead of null because Velocity does not like null
- * when used in #set.
- */
- public String genFetch(ORMGenTable table) {
- return "";
- }
-
- public String genFetchXml(ORMGenTable table) {
- return "";
- }
-
- /**
- * XML Mapping File Path
- */
- public void setXmlMappingFile(String xmlMappingFile) {
- setProperty(XML_MAPPING_FILE, xmlMappingFile, null, null);
- }
-
- /**
- * XML Mapping File Path
- */
- public String getXmlMappingFile() {
- String xmlMappingFile = getProperty(XML_MAPPING_FILE, null, null);
- return xmlMappingFile == null ? DEFAULT_XML_MAPPING_FILE : xmlMappingFile;
- }
-
- public void setPlatformVersion(String platformVersion) {
- setProperty(PLATFORM_VERSION_PROPERTY, platformVersion, null, null);
- }
-
- /**
- * Called when the table user selection is changed in the
- * generation wizard.
- */
- public void setTableNames(List<String> tableNames) {
- mTableNames = tableNames;
- mTables = null;
- mValidAssociations = null; //recompute
- mInvalidForeignAssociations = true; //make sure foreign associations from newly added tables are computed.
- }
- /**
- * Returns the table names to be generated.
- * This might be different from <code>getTableNames</code> if there
- * are many-to-many join tables and are not contributing
- * in any other associations.
- */
- public List<String> getGenTableNames() {
- List<String> names = getTableNames();
- List<String> result = new java.util.ArrayList<String>(names.size());
-
- /*filter out join tables*/
- List<Association> associations = getAssociations();
- for (Iterator<String> tableNamesIter = names.iterator(); tableNamesIter.hasNext(); ) {
- String tableName = tableNamesIter.next();
- boolean isValid = true;
-
- for (Iterator<Association> assocIter = associations.iterator(); assocIter.hasNext(); ) {
- Association association = assocIter.next();
- if (!association.isGenerated()) {
- continue;
- }
- if (tableName.equals(association.getReferrerTableName())
- || tableName.equals(association.getReferencedTableName())) {
- isValid = true;
- break;
- }
- if (tableName.equals(association.getJoinTableName())) {
- isValid = false;
- }
- }
- if (isValid) {
- result.add(tableName);
- }
- }
- return result;
- }
- /**
- * Returns an <code>ORMGenTable</code> object given its name, or
- * null if none.
- */
- public ORMGenTable getTable(String tableName) {
- if (mTables == null) {
- mTables = new java.util.HashMap<String, ORMGenTable>(mTableNames.size());
- }
-
- if(mTableNames!=null && mSchema!=null){
- for (Iterator<String> iter = mTableNames.iterator(); iter.hasNext(); ) {
- String name = iter.next();
- Table dbTable = mSchema.getTableNamed( name );
- if (dbTable != null) {
- mTables.put(name, createGenTable(dbTable));
- }
- }
- }
- return mTables.get(tableName);
- }
- /**
- * Returns the <code>Association</code> objects sorted by their "from"
- * table name.
- */
- public List<Association> getAssociations(){
- return getAssociations(true/*validOnly*/);
- }
- /**
- * Adds the given association.
- */
- public void addAssociation(Association association) {
- getAssociations(false/*validOnly*/).add(association);
- if (mValidAssociations != null) {
- mValidAssociations.add(association);
- }
-
- }
- /**
- * Deletes the given association.
- */
- public void deleteAssociation(Association association) {
- boolean removed = getAssociations(false/*validOnly*/).remove(association);
- assert(removed);
-
- if (mValidAssociations != null) {
- removed = mValidAssociations.remove(association);
- assert(removed);
- }
- }
- /**
- * Returns true if an association similar to the given association
- * already exists.
- * This is decided based only on the association tables and columns.
- */
- public boolean similarAssociationExists(Association association) {
- try {
- for (Iterator<Association> iter = getAssociations(false/*validOnly*/).iterator(); iter.hasNext(); ) {
- Association association2 = iter.next();
- if (!association.getReferrerTableName().equals(association2.getReferrerTableName())
- || !association.getReferencedTableName().equals(association2.getReferencedTableName())
- || !StringUtil.equalObjects(association.getJoinTableName(), association2.getJoinTableName())
- || !association.getReferrerColumnNames().equals(association2.getReferrerColumnNames())
- || !association.getReferencedColumnNames().equals(association2.getReferencedColumnNames())
- ) {
- continue;
- }
- /*the 2 association have the same referrer, referenced and join table*/
- if (association.getJoinTableName() == null) {
- return true;
- }
- if (association.getReferrerJoinColumnNames().equals(association2.getReferrerJoinColumnNames())
- && association.getReferencedJoinColumnNames().equals(association2.getReferencedJoinColumnNames())) {
- return true;
- }
- }
- } catch (Exception e) {
- return false;
- }
- return false;
- }
- /**
- * Creates the <code>ORMGenTable</code> instance.
- */
- public ORMGenTable createGenTable(Table dbTable) {
- return new ORMGenTable(dbTable, this);
- }
- /**
- * Creates the <code>ORMGenColumn</code> instance.
- */
- protected ORMGenColumn createGenColumn(Column dbCol) {
- return new ORMGenColumn(dbCol, this);
- }
- /**
- * Returns true of the underlying persistence specs require the "many" side
- * of an association to be the owner (like EJB3).
- */
- protected boolean manySideIsAssociationOwner() {
- return false;
- }
- public boolean isUpdateConfigFile() {
- return !"false".equals(getProperty(UPDATE_CONFIG_FILE, null, null)); //defaults to true
- }
- public void setUpdateConfigFile(boolean value) {
- if (value) { //default is true
- setProperty(UPDATE_CONFIG_FILE, null, null, null); //remove it
- } else {
- setBooleanProperty(UPDATE_CONFIG_FILE, value, null, null);
- }
- }
-
- //-----------------------------------------
- //---- Velocity templates methods
- //-----------------------------------------
- /**
- * Returns a getter method name given a property name.
- */
- public String propertyGetter(String propertyName) {
- return "get"+StringUtil.initUpper(propertyName);
- }
- /**
- * Returns a setter method name given a property name.
- */
- public String propertySetter(String propertyName) {
- return "set"+StringUtil.initUpper(propertyName);
- }
- public String quote(String s) {
- return StringUtil.quote(s, '"');
- }
- public String quote(boolean b) {
- return quote(String.valueOf(b));
- }
- public String quote(int i) {
- return quote(String.valueOf(i));
- }
- public String convertToJavaStringLiteral(String s) {
- return StringTools.convertToJavaStringLiteral(s);
- }
- public String convertToXmlStringLiteral(String s) {
- return StringTools.convertToXmlAttributeValue(s);
- }
- /**
- * Appends an annotation member name and value to an existing annotation.
- *
- * @param s The annotation members string.
- *
- * @param memberValue The member value, if null or empty strings then
- * nothing is appened.
- *
- * @param whether to double quote the member value.
- */
- public String appendAnnotation(String s, String memberName, String memberValue, boolean quote) {
- if (memberValue == null || memberValue.length() == 0) {
- return s;
- }
- StringBuffer buffer = new StringBuffer(s);
- if (buffer.length() != 0) {
- buffer.append(", ");
- }
- buffer.append(memberName);
- buffer.append('=');
- if (quote) {
- buffer.append('"');
- }
- buffer.append(memberValue);
- if (quote) {
- buffer.append('"');
- }
- return buffer.toString();
- }
-
- /**
- * Appends an attribute name and value to an existing element.
- *
- * @param s The attribute key string.
- *
- * @param memberValue The attribute value, if null or empty strings then
- * nothing is appened.
- *
- * @param whether to double quote the member value.
- */
- public String appendAttribute(String s, String memberName, String memberValue, boolean quote) {
- if (memberValue == null || memberValue.length() == 0) {
- return s;
- }
- StringBuffer buffer = new StringBuffer(s);
- if (buffer.length() != 0) {
- buffer.append(" ");
- }
- buffer.append(memberName);
- buffer.append('=');
- if (quote) {
- buffer.append('"');
- }
- buffer.append(memberValue);
- if (quote) {
- buffer.append('"');
- }
- return buffer.toString();
- }
-
- public boolean isJDK1_5() {
- return true;
- }
-
- /* Get the platform version
- *
- */
- public String getPlatformVersion() {
- return getProperty(PLATFORM_VERSION_PROPERTY, null, null);
- }
-
- public String getPlatformVersionWithUnderscore() {
- String version = getProperty(PLATFORM_VERSION_PROPERTY, null, null);
- return version.replace('.', '_');
- }
-
- //-----------------------------------------
- //---- private methods
- //-----------------------------------------
- /**
- * Restores the customization settings from the given
- * (persisted) customizer.
- */
- private void restore(ORMGenCustomizer customizer) {
- mTableNames = customizer.mTableNames;
- mAssociations = customizer.mAssociations;
- mProps = customizer.mProps;
- mUpdatePersistenceXml = customizer.mUpdatePersistenceXml;
- if( mSchema == null )
- return;
-
- /*remove invalid table names*/
- for (int i = mTableNames.size()-1; i >= 0; --i) {
- String tableName = mTableNames.get(i);
- if (mSchema.getTableNamed( tableName) == null) {
- mTableNames.remove(i);
- }
- }
- if( mAssociations!=null ){
- /*restore the associations*/
- for (Iterator<Association> iter = mAssociations.iterator(); iter.hasNext(); ) {
- Association association = iter.next();
- association.restore(this);
- }
- /*add the foreign keys associations just in case the tables changed since
- * the last time the state was persisted. Pass checkExisting true so that the
- * associations restored above are not overwritten.*/
- addForeignKeyAssociations(true/*checkExisting*/);
- // sort on restore
- sortAssociations( mAssociations );
- }
- }
- /**
- * Returns the key in mProps corresponding to the specified
- * propertyName, table and column.
- */
- private String getPropKey(String propertyName, String tableName, String colName) {
- if (tableName == null) {
- tableName = NULL_TABLE;
- }
- if (colName == null) {
- colName = NULL_COLUMN;
- }
- return tableName + '.' + colName + '.' + propertyName;
- }
- /**
- * Returns the associations that are valid for the
- * current tables.
- */
- private List<Association> getAssociations(boolean validOnly){
- if (mAssociations == null) {
- mAssociations = new java.util.ArrayList<Association>();
-
- addForeignKeyAssociations(false/*checkExisting*/);
- } else if (mInvalidForeignAssociations) {
- mInvalidForeignAssociations = false;
-
- addForeignKeyAssociations(true/*checkExisting*/);
- }
- List<Association> associations;
- if (validOnly) {
- if (mValidAssociations == null) {
- /*filter out the invalid associations*/
- mValidAssociations = new ArrayList<Association>(mAssociations.size());
- for (int i = 0, n = mAssociations.size(); i < n; ++i) {
- Association association = mAssociations.get(i);
- if (association.isValid()) {
- mValidAssociations.add(association);
- }
- }
- }
- associations = mValidAssociations;
- } else {
- associations = mAssociations;
- }
- return associations;
- }
- private void addForeignKeyAssociations(boolean checkExisting) {
- List<String> tableNames = getTableNames();
- for (Iterator<String> iter = tableNames.iterator(); iter.hasNext(); ) {
- ORMGenTable table = getTable(iter.next());
- addForeignKeyAssociations(table, checkExisting);
- }
- }
- private void addForeignKeyAssociations(ORMGenTable table, boolean checkExisting) {
- if(table==null)
- return;
-
-
- List<ForeignKeyInfo> fKeys = null;
-
- try{
- fKeys = DTPUtil.getForeignKeys(table.getDbTable());
- }catch(Exception ise){
- //workaround Dali bug for now
- return;
- }
-
- if( fKeys.size()==0 )
- return;
-
- List<Association> addedAssociations = new java.util.ArrayList<Association>();
- for (Iterator<ForeignKeyInfo> iter = fKeys.iterator(); iter.hasNext(); ) {
- ForeignKeyInfo fki = iter.next();
- ORMGenTable referencedTable = getTable(fki.getReferencedTableName());
- if (referencedTable == null) {
- continue;
- }
- Association association = new Association(this, table.getName(), fki.getReferrerColumnNames()
- , referencedTable.getName(), fki.getReferencedColumnNames());
- association.computeCardinality();
- //Defer the check of similarAssociationExists after computeManyToMany()
- //otherwise the MTM association will not computed correctly in some cases.
- //if (checkExisting && similarAssociationExists(association)) {
- // continue;
- //}
- addedAssociations.add(association);
- }
-
- Association m2m = computeManyToMany(table, addedAssociations);
- if (m2m != null) {
- /*do not generate the 2 many-to-one*/
- addedAssociations.clear();
- addedAssociations.add(0, m2m);
- }
- //remove the association if already existing
- Iterator<Association> it = addedAssociations.iterator();
- while( it.hasNext() ){
- Association newAssociation = it.next();
- for( Association association : mAssociations ){
- if( newAssociation.equals( association )){
- it.remove();
- break;
- }
- }
- }
- mAssociations.addAll(addedAssociations);
- }
- private Association computeManyToMany(ORMGenTable table, List<Association> addedAssociations) {
- /** many-to-many associations if:
- * - addedAssociations contains 2 many-to-one associations
- * - tables t1 and t2 does NOT have to be different( for self-MTM-self situation)
- * - <code>table</code> contains only the foreign key columns.
- *
- * Note: following restrictions have been removed:
- * -table has only two columns
- * -t1 and t2 must be different
- * -the primary key of <code>table</code> is the concatenation of its foreign
- * keys to t1 and t2.*/
-
- if (addedAssociations.size() != 2) {
- return null;
- }
-
- //MTM table should have two MTO relations to orginal tables
- Association assoc1 = addedAssociations.get(0);
- Association assoc2 = addedAssociations.get(1);
- if (assoc1.getCardinality() != Association.MANY_TO_ONE
- || assoc2.getCardinality() != Association.MANY_TO_ONE) {
- return null;
- }
-
- //MTM table should only include foreign key columns
- for( ORMGenColumn col : table.getColumns()){
- if( !col.isForeignKey())
- return null;
- }
-
-
- ORMGenTable t1 = assoc1.getReferencedTable();
- ORMGenTable t2 = assoc2.getReferencedTable();
-
- if( t1.getName().equals(table.getName()) || t2.getName().equals(table.getName()) ) {
- return null;
- }
-
- //Make a guess which table is the owning side of the MTM relation
- //See https://bugs.eclipse.org/bugs/show_bug.cgi?id=268445
- //Logic borrowed from DTPTableWrapper.getJoinTableOwningForeignKey()
- if( !table.getName().equals(t1.getName() + "_" + t2.getName() ) ) {
- //swap t1 and t2
- ORMGenTable t3 = t1;
- t1=t2;
- t2=t3;
- //swap assoc1 and assoc2
- Association assoc3=assoc1;
- assoc1=assoc2;
- assoc2=assoc3;
- }
-
-//Commented out because the assumption is too restrictive:
-//this check will prevent generating MTM mapping table not having
-//primary key defined
-// List pkNames = DTPUtil.getPrimaryKeyColumnNames(table.getDbTable());
-// if (pkNames.size() != table.getColumnNames().size()) {
-// return null;
-// }
-// List fkNames = new java.util.ArrayList(assoc1.getReferrerColumnNames()); //clone because we modify by addAll below
-// fkNames.addAll(assoc2.getReferrerColumnNames());
-// if (!CollectionUtil.equalsIgnoreOrder(pkNames, fkNames)) {
-// return null;
-// }
- Association m2m = new Association(this, t1.getName()/*referrerTableName*/, assoc1.getReferencedColumnNames()/*referrerColNames*/
- , t2.getName()/*referencedTableName*/, assoc2.getReferencedColumnNames()/*referencedColNames*/
- , table.getName(), assoc1.getReferrerColumnNames()/*referrerJoinColNames*/, assoc2.getReferrerColumnNames()/*referencedJoinColNames*/);
- m2m.setCustom(false);
- return m2m;
- }
-
- //---------------------------------------------------
- //---- FileHeader class -----------------------------
- //---------------------------------------------------
- /**
- * The header of the customization file.
- */
- private static class FileHeader implements java.io.Serializable
- {
- private static final long serialVersionUID = 1L;
- /**
- * Should be argument-less because it is used in
- * the de-serialization process.
- */
- public FileHeader() {
- mVersion = FILE_VERSION;
- }
- int mVersion;
- }
-
- private void sortAssociations( List< Association > list ) {
- Collections.sort( list, new Comparator< Association >() {
- public int compare( Association lhs, Association rhs ) {
- // sort by referrer table name first...
- int test = lhs.getReferrerTableName().compareTo( rhs.getReferrerTableName() );
- if( test != 0 )
- return test;
- // then by referenced table name...
- test = lhs.getReferencedTableName().compareTo( rhs.getReferencedTableName() );
- if( test != 0 )
- return test;
- // if referrer and referenced tables are the same, they should
- // appear next to each other
- return 0;
- }
- } );
- }
- public boolean updatePersistenceXml() {
- return mUpdatePersistenceXml;
- }
- public void setUpdatePersistenceXml(boolean updatePersistenceXml) {
- this.mUpdatePersistenceXml = updatePersistenceXml;
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2007, 2012 Oracle. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0, which accompanies this distribution
+ * and is available at http://www.eclipse.org/legal/epl-v10.html.
+ *
+ * Contributors:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.jpa.gen.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.eclipse.jpt.common.utility.internal.StringTools;
+import org.eclipse.jpt.jpa.db.Column;
+import org.eclipse.jpt.jpa.db.Schema;
+import org.eclipse.jpt.jpa.db.Table;
+import org.eclipse.jpt.jpa.gen.internal.plugin.JptJpaGenPlugin;
+import org.eclipse.jpt.jpa.gen.internal.util.DTPUtil;
+import org.eclipse.jpt.jpa.gen.internal.util.FileUtil;
+import org.eclipse.jpt.jpa.gen.internal.util.ForeignKeyInfo;
+import org.eclipse.jpt.jpa.gen.internal.util.StringUtil;
+
+/**
+ * Contains the information used to customize the database schema to ORM entity
+ * generation.
+ *
+ * <p>The customization settings are mainly exposed in the form of
+ * properties. There are no assumptions in this class about the meaning of the
+ * property names. Properties can be associated to specific tables and table
+ * columns, or globally for any table and/or column.
+ *
+ * <p>Subclass can implement the sets of abstract methods to provide ORM vendor
+ * specific properties.
+ *
+ */
+public abstract class ORMGenCustomizer implements java.io.Serializable
+{
+ private final static long serialVersionUID = 1;
+
+ /**
+ * A value passed for the table name argument to get/setProperty
+ * indicating that the value applies to any table.
+ */
+ public static final String ANY_TABLE = "__anyTable__";
+ public static final String GENERATE_DDL_ANNOTATION = "generateDDLAnnotations";
+ /*the string used in the property name in mProps to indicate
+ * a null table value.*/
+ private static final String NULL_TABLE = "";
+ /*the string used in the property name in mProps to indicate
+ * a null column value.*/
+ private static final String NULL_COLUMN = "";
+
+ /*This version number is written in the header of the customization stream
+ * and read at de-serialization time, if it is different then the file is invalidated.
+ */
+ private static final int FILE_VERSION = 2;
+
+ private static final String UPDATE_CONFIG_FILE = "updateConfigFile";
+
+ private transient Schema mSchema;
+ private transient File mFile;
+
+ private List<String> mTableNames;
+ /*key: table name, value: ORMGenTable object.*/
+ private transient Map<String , ORMGenTable> mTables;
+ /*the <code>Association</code> objects sorted by their "from"
+ * table name. Includes all association derived from foreign keys
+ * in user selected tables. Since some of the foreign keys may point to table
+ * user does not select, this list may be different from mValidAssociations
+ */
+ private List<Association> mAssociations;
+ /*
+ * List of valid associations within the user selected tables
+ */
+ private transient List<Association> mValidAssociations;
+ private transient boolean mInvalidForeignAssociations;
+
+ /*the property name is in the form $tableName.$columnName.$propertyName.
+ * Where tableName could be NULL_TABLE or ANY_TABLE
+ * and columnName could be NULL_COLUMN*/
+ private Map<String, String> mProps = new java.util.HashMap<String, String>();
+
+ private transient DatabaseAnnotationNameBuilder databaseAnnotationNameBuilder = DatabaseAnnotationNameBuilder.Default.INSTANCE;
+
+ private boolean mUpdatePersistenceXml = true;
+
+ //EclipseLink mapping file default name
+ private static final String DEFAULT_XML_MAPPING_FILE = "META-INF/eclipselink-orm.xml";
+ private static final String XML_MAPPING_FILE = "xmlMappingFileName";
+
+ private static final String PLATFORM_VERSION_PROPERTY = "platformVersion";
+
+ //-----------------------------------------
+ //---- abstract methods
+ //-----------------------------------------
+ /**
+ * Returns all the primary key generator schemes.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract List<String> getAllIdGenerators();
+ /**
+ * Returns the string representing the developer-assigned id generator.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String getNoIdGenerator();
+ /**
+ * Returns the string representing the identity id generator.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String getIdentityIdGenerator();
+ /**
+ * Returns the strings representing the sequence generators.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract Set<String> getSequenceIdGenerators();
+ /**
+ * Returns a property type from the given database column.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String getPropertyTypeFromColumn(Column column) ;
+ /**
+ * Returns all the strings representing property types.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String[] getAllPropertyTypes();
+ /**
+ * Returns all the strings representing property mapping kinds.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String[] getAllMappingKinds();
+ /**
+ * Returns the basic (default) property mapping kind.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String getBasicMappingKind();
+ /**
+ * Returns the id (primary key) property mapping kind.
+ * This can return any strings as far as the Velocity template
+ * processor understand them.
+ */
+ public abstract String getIdMappingKind();
+ /**
+ * Interacts with the user to edit the cascade of the given
+ * role.
+ * This method should also call <code>AssociationRole.setCascade</code>.
+ *
+ * @return false if the user interaction is cancelled.
+ */
+ public abstract boolean editCascade(AssociationRole role);
+
+ //-----------------------------------------
+ //-----------------------------------------
+
+ /**
+ * @param file The file that contains the customization settings.
+ * The file is created if necessary when the <code>save</code>
+ * method is called.
+ */
+ public void init( File file, Schema schema) {
+ this.mSchema = schema;
+ mFile = file;
+
+ if (!file.exists()) {
+ setProperty(ORMGenTable.DEFAULT_FETCH, ORMGenTable.DEFAULT_FETCH, ORMGenCustomizer.ANY_TABLE, null);
+ return;
+ }
+ InputStream istream = null;
+ ObjectInputStream ois = null;
+ try
+ {
+ //read it in a file first to speedup deserialization
+ byte[] bytes = FileUtil.readFile(file);
+ istream = new ByteArrayInputStream(bytes);
+ ois = new ObjectInputStream(istream);
+
+ FileHeader header = (FileHeader)ois.readObject();
+ if (header.mVersion == FILE_VERSION) {
+ ORMGenCustomizer customizer = (ORMGenCustomizer)ois.readObject();
+ restore(customizer);
+ }
+ } catch (Exception ex) {
+ JptJpaGenPlugin.instance().logError(ex, "***ORMGenCustomizer.load failed {0}", file); //$NON-NLS-1$
+ }
+ finally
+ {
+ if (ois != null)
+ {
+ try
+ {
+ ois.close();
+ } catch (IOException e) {
+ }
+ }
+
+ if (istream != null)
+ {
+ try
+ {
+ istream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ public File getFile(){
+ return this.mFile;
+ }
+
+ public void setSchema(Schema schema){
+ this.mSchema = schema;
+ }
+
+ public Schema getSchema(){
+ return mSchema;
+ }
+
+ /**
+ * Empty constructor needed by the deserialization.
+ */
+ public ORMGenCustomizer() {
+ super();
+ }
+
+ /**
+ * Saves the customization file.
+ * The file is created if necessary.
+ */
+ public void save() throws IOException {
+ //System.out.println("---ORMGenCustomizer.save: " + mFile);
+ if (!mFile.exists() && !mFile.createNewFile()) {
+ return;
+ }
+ java.io.FileOutputStream fos = null;
+ java.io.ObjectOutputStream oos = null;
+ boolean deleteIt = true;
+ try {
+ fos = new java.io.FileOutputStream(mFile);
+ oos = new java.io.ObjectOutputStream(fos);
+ FileHeader header = new FileHeader();
+ oos.writeObject(header);
+ oos.writeObject(this);
+ deleteIt = false;
+ } catch (Exception ex) {
+ //deleteIt is true, so the cache is not saved.
+ JptJpaGenPlugin.instance().logError(ex, "Unable to save the ORMGenCustomizer file: {0}", mFile); //$NON-NLS-1$
+ } finally {
+ try {
+ if (oos!=null) oos.close();
+ if (fos!=null) fos.close();
+ if (deleteIt) {
+ mFile.delete();
+ }
+ } catch (java.io.IOException ex2) {}
+ }
+ }
+
+ public DatabaseAnnotationNameBuilder getDatabaseAnnotationNameBuilder() {
+ return this.databaseAnnotationNameBuilder;
+ }
+ public void setDatabaseAnnotationNameBuilder(DatabaseAnnotationNameBuilder databaseAnnotationNameBuilder) {
+ if (databaseAnnotationNameBuilder == null) {
+ throw new NullPointerException("database annotation name builder is required"); //$NON-NLS-1$
+ }
+ this.databaseAnnotationNameBuilder = databaseAnnotationNameBuilder;
+ }
+
+ /**
+ * Returns {@link #GENERATE_DDL_ANNOTATION} indicating whether
+ * the optional DDL parameters like length, nullable, unqiue, etc should be generated
+ * in @Column annotation.
+ * defaults to false.
+ */
+ public boolean isGenerateDDLAnnotations() {
+ return "true".equals(getProperty(GENERATE_DDL_ANNOTATION, ANY_TABLE, null)); //defaults to false
+ }
+
+ /**
+ * Returns a property value.
+ */
+ public String getProperty(String propertyName, String tableName, String colName) {
+ String key = getPropKey(propertyName, tableName, colName);
+ String value = mProps.get(key);
+ /*if the key does not exist and it is a table property then
+ * get the default table property.*/
+ if (value == null && tableName != null && colName == null && !tableName.equals(ANY_TABLE)) {
+ value = getProperty(propertyName, ANY_TABLE, colName);
+ }
+ return value;
+ }
+ /**
+ * Changes a property value.
+ *
+ * @param value The new value, could be null.
+ */
+ public void setProperty(String propertyName, String value, String tableName, String colName) {
+ String key = getPropKey(propertyName, tableName, colName);
+ if (value != null) {
+ mProps.put(key, value);
+ } else {
+ mProps.remove(key);
+ }
+ }
+ /**
+ * Same as {@link #getProperty(String, String, String)} but
+ * converts the value to boolean.
+ */
+ public boolean getBooleanProperty(String propertyName, String tableName, String colName) {
+ String value = getProperty(propertyName, tableName, colName);
+ return "true".equals(value);
+ }
+ /**
+ * Changes a table boolean property value.
+ */
+ public void setBooleanProperty(String propertyName, boolean value, String tableName, String colName) {
+ setProperty(propertyName, value ? "true" : "false", tableName, colName);
+ }
+ /**
+ * Returns the names of the tables to generate.
+ */
+ @SuppressWarnings("unchecked")
+ public List<String> getTableNames() {
+ return mTableNames != null ? mTableNames : java.util.Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Returns the fetch type annotation member value, or empty string
+ * if none.
+ * Empty string is returned instead of null because Velocity does not like null
+ * when used in #set.
+ */
+ public String genFetch(ORMGenTable table) {
+ return "";
+ }
+
+ public String genFetchXml(ORMGenTable table) {
+ return "";
+ }
+
+ /**
+ * XML Mapping File Path
+ */
+ public void setXmlMappingFile(String xmlMappingFile) {
+ setProperty(XML_MAPPING_FILE, xmlMappingFile, null, null);
+ }
+
+ /**
+ * XML Mapping File Path
+ */
+ public String getXmlMappingFile() {
+ String xmlMappingFile = getProperty(XML_MAPPING_FILE, null, null);
+ return xmlMappingFile == null ? DEFAULT_XML_MAPPING_FILE : xmlMappingFile;
+ }
+
+ public void setPlatformVersion(String platformVersion) {
+ setProperty(PLATFORM_VERSION_PROPERTY, platformVersion, null, null);
+ }
+
+ /**
+ * Called when the table user selection is changed in the
+ * generation wizard.
+ */
+ public void setTableNames(List<String> tableNames) {
+ mTableNames = tableNames;
+ mTables = null;
+ mValidAssociations = null; //recompute
+ mInvalidForeignAssociations = true; //make sure foreign associations from newly added tables are computed.
+ }
+ /**
+ * Returns the table names to be generated.
+ * This might be different from <code>getTableNames</code> if there
+ * are many-to-many join tables and are not contributing
+ * in any other associations.
+ */
+ public List<String> getGenTableNames() {
+ List<String> names = getTableNames();
+ List<String> result = new java.util.ArrayList<String>(names.size());
+
+ /*filter out join tables*/
+ List<Association> associations = getAssociations();
+ for (Iterator<String> tableNamesIter = names.iterator(); tableNamesIter.hasNext(); ) {
+ String tableName = tableNamesIter.next();
+ boolean isValid = true;
+
+ for (Iterator<Association> assocIter = associations.iterator(); assocIter.hasNext(); ) {
+ Association association = assocIter.next();
+ if (!association.isGenerated()) {
+ continue;
+ }
+ if (tableName.equals(association.getReferrerTableName())
+ || tableName.equals(association.getReferencedTableName())) {
+ isValid = true;
+ break;
+ }
+ if (tableName.equals(association.getJoinTableName())) {
+ isValid = false;
+ }
+ }
+ if (isValid) {
+ result.add(tableName);
+ }
+ }
+ return result;
+ }
+ /**
+ * Returns an <code>ORMGenTable</code> object given its name, or
+ * null if none.
+ */
+ public ORMGenTable getTable(String tableName) {
+ if (mTables == null) {
+ mTables = new java.util.HashMap<String, ORMGenTable>(mTableNames.size());
+ }
+
+ if(mTableNames!=null && mSchema!=null){
+ for (Iterator<String> iter = mTableNames.iterator(); iter.hasNext(); ) {
+ String name = iter.next();
+ Table dbTable = mSchema.getTableNamed( name );
+ if (dbTable != null) {
+ mTables.put(name, createGenTable(dbTable));
+ }
+ }
+ }
+ return mTables.get(tableName);
+ }
+ /**
+ * Returns the <code>Association</code> objects sorted by their "from"
+ * table name.
+ */
+ public List<Association> getAssociations(){
+ return getAssociations(true/*validOnly*/);
+ }
+ /**
+ * Adds the given association.
+ */
+ public void addAssociation(Association association) {
+ getAssociations(false/*validOnly*/).add(association);
+ if (mValidAssociations != null) {
+ mValidAssociations.add(association);
+ }
+
+ }
+ /**
+ * Deletes the given association.
+ */
+ public void deleteAssociation(Association association) {
+ boolean removed = getAssociations(false/*validOnly*/).remove(association);
+ assert(removed);
+
+ if (mValidAssociations != null) {
+ removed = mValidAssociations.remove(association);
+ assert(removed);
+ }
+ }
+ /**
+ * Returns true if an association similar to the given association
+ * already exists.
+ * This is decided based only on the association tables and columns.
+ */
+ public boolean similarAssociationExists(Association association) {
+ try {
+ for (Iterator<Association> iter = getAssociations(false/*validOnly*/).iterator(); iter.hasNext(); ) {
+ Association association2 = iter.next();
+ if (!association.getReferrerTableName().equals(association2.getReferrerTableName())
+ || !association.getReferencedTableName().equals(association2.getReferencedTableName())
+ || !StringUtil.equalObjects(association.getJoinTableName(), association2.getJoinTableName())
+ || !association.getReferrerColumnNames().equals(association2.getReferrerColumnNames())
+ || !association.getReferencedColumnNames().equals(association2.getReferencedColumnNames())
+ ) {
+ continue;
+ }
+ /*the 2 association have the same referrer, referenced and join table*/
+ if (association.getJoinTableName() == null) {
+ return true;
+ }
+ if (association.getReferrerJoinColumnNames().equals(association2.getReferrerJoinColumnNames())
+ && association.getReferencedJoinColumnNames().equals(association2.getReferencedJoinColumnNames())) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ return false;
+ }
+ /**
+ * Creates the <code>ORMGenTable</code> instance.
+ */
+ public ORMGenTable createGenTable(Table dbTable) {
+ return new ORMGenTable(dbTable, this);
+ }
+ /**
+ * Creates the <code>ORMGenColumn</code> instance.
+ */
+ protected ORMGenColumn createGenColumn(Column dbCol) {
+ return new ORMGenColumn(dbCol, this);
+ }
+ /**
+ * Returns true of the underlying persistence specs require the "many" side
+ * of an association to be the owner (like EJB3).
+ */
+ protected boolean manySideIsAssociationOwner() {
+ return false;
+ }
+ public boolean isUpdateConfigFile() {
+ return !"false".equals(getProperty(UPDATE_CONFIG_FILE, null, null)); //defaults to true
+ }
+ public void setUpdateConfigFile(boolean value) {
+ if (value) { //default is true
+ setProperty(UPDATE_CONFIG_FILE, null, null, null); //remove it
+ } else {
+ setBooleanProperty(UPDATE_CONFIG_FILE, value, null, null);
+ }
+ }
+
+ //-----------------------------------------
+ //---- Velocity templates methods
+ //-----------------------------------------
+ /**
+ * Returns a getter method name given a property name.
+ */
+ public String propertyGetter(String propertyName) {
+ return "get"+StringUtil.initUpper(propertyName);
+ }
+ /**
+ * Returns a setter method name given a property name.
+ */
+ public String propertySetter(String propertyName) {
+ return "set"+StringUtil.initUpper(propertyName);
+ }
+ public String quote(String s) {
+ return StringUtil.quote(s, '"');
+ }
+ public String quote(boolean b) {
+ return quote(String.valueOf(b));
+ }
+ public String quote(int i) {
+ return quote(String.valueOf(i));
+ }
+ public String convertToJavaStringLiteral(String s) {
+ return StringTools.convertToJavaStringLiteral(s);
+ }
+ public String convertToXmlStringLiteral(String s) {
+ return StringTools.convertToXmlAttributeValue(s);
+ }
+ /**
+ * Appends an annotation member name and value to an existing annotation.
+ *
+ * @param s The annotation members string.
+ *
+ * @param memberValue The member value, if null or empty strings then
+ * nothing is appened.
+ *
+ * @param whether to double quote the member value.
+ */
+ public String appendAnnotation(String s, String memberName, String memberValue, boolean quote) {
+ if (memberValue == null || memberValue.length() == 0) {
+ return s;
+ }
+ StringBuffer buffer = new StringBuffer(s);
+ if (buffer.length() != 0) {
+ buffer.append(", ");
+ }
+ buffer.append(memberName);
+ buffer.append('=');
+ if (quote) {
+ buffer.append('"');
+ }
+ buffer.append(memberValue);
+ if (quote) {
+ buffer.append('"');
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Appends an attribute name and value to an existing element.
+ *
+ * @param s The attribute key string.
+ *
+ * @param memberValue The attribute value, if null or empty strings then
+ * nothing is appened.
+ *
+ * @param whether to double quote the member value.
+ */
+ public String appendAttribute(String s, String memberName, String memberValue, boolean quote) {
+ if (memberValue == null || memberValue.length() == 0) {
+ return s;
+ }
+ StringBuffer buffer = new StringBuffer(s);
+ if (buffer.length() != 0) {
+ buffer.append(" ");
+ }
+ buffer.append(memberName);
+ buffer.append('=');
+ if (quote) {
+ buffer.append('"');
+ }
+ buffer.append(memberValue);
+ if (quote) {
+ buffer.append('"');
+ }
+ return buffer.toString();
+ }
+
+ public boolean isJDK1_5() {
+ return true;
+ }
+
+ /* Get the platform version
+ *
+ */
+ public String getPlatformVersion() {
+ return getProperty(PLATFORM_VERSION_PROPERTY, null, null);
+ }
+
+ public String getPlatformVersionWithUnderscore() {
+ String version = getProperty(PLATFORM_VERSION_PROPERTY, null, null);
+ return version.replace('.', '_');
+ }
+
+ //-----------------------------------------
+ //---- private methods
+ //-----------------------------------------
+ /**
+ * Restores the customization settings from the given
+ * (persisted) customizer.
+ */
+ private void restore(ORMGenCustomizer customizer) {
+ mTableNames = customizer.mTableNames;
+ mAssociations = customizer.mAssociations;
+ mProps = customizer.mProps;
+ mUpdatePersistenceXml = customizer.mUpdatePersistenceXml;
+ if( mSchema == null )
+ return;
+
+ /*remove invalid table names*/
+ for (int i = mTableNames.size()-1; i >= 0; --i) {
+ String tableName = mTableNames.get(i);
+ if (mSchema.getTableNamed( tableName) == null) {
+ mTableNames.remove(i);
+ }
+ }
+ if( mAssociations!=null ){
+ /*restore the associations*/
+ for (Iterator<Association> iter = mAssociations.iterator(); iter.hasNext(); ) {
+ Association association = iter.next();
+ association.restore(this);
+ }
+ /*add the foreign keys associations just in case the tables changed since
+ * the last time the state was persisted. Pass checkExisting true so that the
+ * associations restored above are not overwritten.*/
+ addForeignKeyAssociations(true/*checkExisting*/);
+ // sort on restore
+ sortAssociations( mAssociations );
+ }
+ }
+ /**
+ * Returns the key in mProps corresponding to the specified
+ * propertyName, table and column.
+ */
+ private String getPropKey(String propertyName, String tableName, String colName) {
+ if (tableName == null) {
+ tableName = NULL_TABLE;
+ }
+ if (colName == null) {
+ colName = NULL_COLUMN;
+ }
+ return tableName + '.' + colName + '.' + propertyName;
+ }
+ /**
+ * Returns the associations that are valid for the
+ * current tables.
+ */
+ private List<Association> getAssociations(boolean validOnly){
+ if (mAssociations == null) {
+ mAssociations = new java.util.ArrayList<Association>();
+
+ addForeignKeyAssociations(false/*checkExisting*/);
+ } else if (mInvalidForeignAssociations) {
+ mInvalidForeignAssociations = false;
+
+ addForeignKeyAssociations(true/*checkExisting*/);
+ }
+ List<Association> associations;
+ if (validOnly) {
+ if (mValidAssociations == null) {
+ /*filter out the invalid associations*/
+ mValidAssociations = new ArrayList<Association>(mAssociations.size());
+ for (int i = 0, n = mAssociations.size(); i < n; ++i) {
+ Association association = mAssociations.get(i);
+ if (association.isValid()) {
+ mValidAssociations.add(association);
+ }
+ }
+ }
+ associations = mValidAssociations;
+ } else {
+ associations = mAssociations;
+ }
+ return associations;
+ }
+ private void addForeignKeyAssociations(boolean checkExisting) {
+ List<String> tableNames = getTableNames();
+ for (Iterator<String> iter = tableNames.iterator(); iter.hasNext(); ) {
+ ORMGenTable table = getTable(iter.next());
+ addForeignKeyAssociations(table, checkExisting);
+ }
+ }
+ private void addForeignKeyAssociations(ORMGenTable table, boolean checkExisting) {
+ if(table==null)
+ return;
+
+
+ List<ForeignKeyInfo> fKeys = null;
+
+ try{
+ fKeys = DTPUtil.getForeignKeys(table.getDbTable());
+ }catch(Exception ise){
+ //workaround Dali bug for now
+ return;
+ }
+
+ if( fKeys.size()==0 )
+ return;
+
+ List<Association> addedAssociations = new java.util.ArrayList<Association>();
+ for (Iterator<ForeignKeyInfo> iter = fKeys.iterator(); iter.hasNext(); ) {
+ ForeignKeyInfo fki = iter.next();
+ ORMGenTable referencedTable = getTable(fki.getReferencedTableName());
+ if (referencedTable == null) {
+ continue;
+ }
+ Association association = new Association(this, table.getName(), fki.getReferrerColumnNames()
+ , referencedTable.getName(), fki.getReferencedColumnNames());
+ association.computeCardinality();
+ //Defer the check of similarAssociationExists after computeManyToMany()
+ //otherwise the MTM association will not computed correctly in some cases.
+ //if (checkExisting && similarAssociationExists(association)) {
+ // continue;
+ //}
+ addedAssociations.add(association);
+ }
+
+ Association m2m = computeManyToMany(table, addedAssociations);
+ if (m2m != null) {
+ /*do not generate the 2 many-to-one*/
+ addedAssociations.clear();
+ addedAssociations.add(0, m2m);
+ }
+ //remove the association if already existing
+ Iterator<Association> it = addedAssociations.iterator();
+ while( it.hasNext() ){
+ Association newAssociation = it.next();
+ for( Association association : mAssociations ){
+ if( newAssociation.equals( association )){
+ it.remove();
+ break;
+ }
+ }
+ }
+ mAssociations.addAll(addedAssociations);
+ }
+ private Association computeManyToMany(ORMGenTable table, List<Association> addedAssociations) {
+ /** many-to-many associations if:
+ * - addedAssociations contains 2 many-to-one associations
+ * - tables t1 and t2 does NOT have to be different( for self-MTM-self situation)
+ * - <code>table</code> contains only the foreign key columns.
+ *
+ * Note: following restrictions have been removed:
+ * -table has only two columns
+ * -t1 and t2 must be different
+ * -the primary key of <code>table</code> is the concatenation of its foreign
+ * keys to t1 and t2.*/
+
+ if (addedAssociations.size() != 2) {
+ return null;
+ }
+
+ //MTM table should have two MTO relations to orginal tables
+ Association assoc1 = addedAssociations.get(0);
+ Association assoc2 = addedAssociations.get(1);
+ if (assoc1.getCardinality() != Association.MANY_TO_ONE
+ || assoc2.getCardinality() != Association.MANY_TO_ONE) {
+ return null;
+ }
+
+ //MTM table should only include foreign key columns
+ for( ORMGenColumn col : table.getColumns()){
+ if( !col.isForeignKey())
+ return null;
+ }
+
+
+ ORMGenTable t1 = assoc1.getReferencedTable();
+ ORMGenTable t2 = assoc2.getReferencedTable();
+
+ if( t1.getName().equals(table.getName()) || t2.getName().equals(table.getName()) ) {
+ return null;
+ }
+
+ //Make a guess which table is the owning side of the MTM relation
+ //See https://bugs.eclipse.org/bugs/show_bug.cgi?id=268445
+ //Logic borrowed from DTPTableWrapper.getJoinTableOwningForeignKey()
+ if( !table.getName().equals(t1.getName() + "_" + t2.getName() ) ) {
+ //swap t1 and t2
+ ORMGenTable t3 = t1;
+ t1=t2;
+ t2=t3;
+ //swap assoc1 and assoc2
+ Association assoc3=assoc1;
+ assoc1=assoc2;
+ assoc2=assoc3;
+ }
+
+//Commented out because the assumption is too restrictive:
+//this check will prevent generating MTM mapping table not having
+//primary key defined
+// List pkNames = DTPUtil.getPrimaryKeyColumnNames(table.getDbTable());
+// if (pkNames.size() != table.getColumnNames().size()) {
+// return null;
+// }
+// List fkNames = new java.util.ArrayList(assoc1.getReferrerColumnNames()); //clone because we modify by addAll below
+// fkNames.addAll(assoc2.getReferrerColumnNames());
+// if (!CollectionUtil.equalsIgnoreOrder(pkNames, fkNames)) {
+// return null;
+// }
+ Association m2m = new Association(this, t1.getName()/*referrerTableName*/, assoc1.getReferencedColumnNames()/*referrerColNames*/
+ , t2.getName()/*referencedTableName*/, assoc2.getReferencedColumnNames()/*referencedColNames*/
+ , table.getName(), assoc1.getReferrerColumnNames()/*referrerJoinColNames*/, assoc2.getReferrerColumnNames()/*referencedJoinColNames*/);
+ m2m.setCustom(false);
+ return m2m;
+ }
+
+ //---------------------------------------------------
+ //---- FileHeader class -----------------------------
+ //---------------------------------------------------
+ /**
+ * The header of the customization file.
+ */
+ private static class FileHeader implements java.io.Serializable
+ {
+ private static final long serialVersionUID = 1L;
+ /**
+ * Should be argument-less because it is used in
+ * the de-serialization process.
+ */
+ public FileHeader() {
+ mVersion = FILE_VERSION;
+ }
+ int mVersion;
+ }
+
+ private void sortAssociations( List< Association > list ) {
+ Collections.sort( list, new Comparator< Association >() {
+ public int compare( Association lhs, Association rhs ) {
+ // sort by referrer table name first...
+ int test = lhs.getReferrerTableName().compareTo( rhs.getReferrerTableName() );
+ if( test != 0 )
+ return test;
+ // then by referenced table name...
+ test = lhs.getReferencedTableName().compareTo( rhs.getReferencedTableName() );
+ if( test != 0 )
+ return test;
+ // if referrer and referenced tables are the same, they should
+ // appear next to each other
+ return 0;
+ }
+ } );
+ }
+ public boolean updatePersistenceXml() {
+ return mUpdatePersistenceXml;
+ }
+ public void setUpdatePersistenceXml(boolean updatePersistenceXml) {
+ this.mUpdatePersistenceXml = updatePersistenceXml;
+ }
+}