/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsplugin.ui.descriptor.relational;
import java.util.*;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext;
import org.eclipse.persistence.tools.workbench.framework.resources.ResourceRepository;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWError;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWColumn;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWDatabase;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWReference;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWTable;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWClassIndicatorPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptorInheritancePolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWMappingDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalTransactionalPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWTableDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWDirectCollectionMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWReferenceMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWAggregateMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWCollectionMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWDirectToFieldMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWManyToManyMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWOneToManyMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWOneToOneMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWRelationalDirectCollectionMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.relational.MWRelationalProject;
import org.eclipse.persistence.tools.workbench.mappingsplugin.ui.common.StatusDialog;
import org.eclipse.persistence.tools.workbench.platformsmodel.DatabaseType;
import org.eclipse.persistence.tools.workbench.utility.string.SimpleStringMatcher;
import org.eclipse.persistence.tools.workbench.utility.string.StringTools;
class TableGenerator
{
private WorkbenchContext workbenchContext;
private Collection classDescriptors;
private HashMap descriptorLookup;
private HashMap tableLookup;
private MWRelationalProject project;
private Vector log;
private HashMap relationshipTableLookup;
private int tableCreatedCount;
protected static HashMap primitiveToJavaTypeMap;
protected final int ATTR_TYPE = 0;
protected final int ITEM_TYPE = 1;
TableGenerator(WorkbenchContext workbenchContext) {
super();
initialize(workbenchContext);
}
protected void initialize(WorkbenchContext workbenchContex) {
this.workbenchContext = workbenchContex;
this.descriptorLookup = new HashMap();
this.tableLookup = new HashMap();
this.relationshipTableLookup = new HashMap();
this.log = new Vector();
}
public StatusDialog.Status generateTablesFromDescriptors(Collection descriptorCollection) {
MWDescriptor firstDescriptor = (MWDescriptor) descriptorCollection.iterator().next();
setProject( (MWRelationalProject) firstDescriptor.getProject() );
// Sort descriptors, superclasses first. Class descriptors only
setClassDescriptors( sortTableDescriptors(descriptorCollection) );
runGenerateTables();
generationCompleted();
if (descriptorCollection.size() == 1)
return StatusDialog.createStatus(firstDescriptor, this.log);
return StatusDialog.createStatus(this.project, this.log);
}
private void generationCompleted() {
if (this.log.isEmpty()) {
this.log.add(new MWError("TABLE_GENERATOR_SUCCESSFUL"));
}
}
protected void runGenerateTables() {
//try {
// PASS 1: CREATE TABLES FOR DESCRIPTORS
// int numberOfDescriptors = getClassDescriptors().size();
int count = 0;
for (Iterator descriptors = getClassDescriptors().iterator(); descriptors.hasNext(); ) {
MWTableDescriptor descriptor = (MWTableDescriptor) descriptors.next();
// MWClass bldrClass = descriptor.getMWClass();
//getProgress().setStatus(resourceRepository().getString("creatingTablesFor", new Object[] { bldrClass.shortName() }));
count++;
createTableIfAbsent(descriptor);
descriptor.setActive(true);
// if((int)(((float)count / (float)numberOfDescriptors) * 100) != 100) {
//getProgress().setPercentComplete((int)(((float)count / (float)numberOfDescriptors) * 100));
// }
}
// PASS 2: NON-COLLECTION VARIABLES
// -- non-collection variables are done before collections so that OneToOne backpointers can
// be created before their corresponding One-To-Many mappings.
count = 0;
//getProgress().setStatus(resourceRepository().getString("mappingNonCollectionVariables"));
for (Iterator descriptors = getClassDescriptors().iterator(); descriptors.hasNext(); ) {
MWTableDescriptor descriptor = (MWTableDescriptor) descriptors.next();
for (Iterator attributes = descriptor.getMWClass().attributes(); attributes.hasNext(); ) {
MWClassAttribute attribute = (MWClassAttribute)attributes.next();
MWMapping mapping = descriptor.mappingForAttribute(attribute);
if (mapping == null) {
descriptor.addDirectMapping(attribute);
}
}
for (Iterator mappings = descriptor.mappings(); mappings.hasNext(); ) {
MWMapping mapping = (MWMapping) mappings.next();
MWClass sourceClass = mapping.getParentDescriptor().getMWClass();
/*MWMapping newMapping = */generateForNonCollectionMapping(mapping, sourceClass);
}
count++;
// if((int)(((float)count / (float)numberOfDescriptors) * 100) != 100) {
//getProgress().setPercentComplete((int)(((float)count / (float)numberOfDescriptors) * 100));
// }
}
// PASS 3: COLLECTION VARIABLES
count = 0;
//getProgress().setStatus(resourceRepository().getString("mappingCollectionVariables"));
for (Iterator descriptors = getClassDescriptors().iterator(); descriptors.hasNext(); ) {
MWTableDescriptor descriptor = (MWTableDescriptor) descriptors.next();
for (Iterator mappings = descriptor.mappings(); mappings.hasNext(); ) {
MWMapping mapping = (MWMapping) mappings.next();
MWClass sourceClass = mapping.getParentDescriptor().getMWClass();
/*MWMapping newMapping = */generateForCollectionMapping( mapping, sourceClass );
}
count++;
// if((int)(((float)count / (float)numberOfDescriptors) * 100) != 100) {
//getProgress().setPercentComplete((int)(((float)count / (float)numberOfDescriptors) * 100));
// }
}
//} catch (Exception e) {
//ThrowableDialog.show( getMainView(), e);
//getProgress().setPercentComplete(100);
//}
//getProgress().setPercentComplete(100);
}
protected MWOneToOneMapping backpointerFor(MWMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass) {
// returnsWith: <Collection element: <BLDRVariableDefinition>>"
MWClass itemType = attrAndItemType[this.ITEM_TYPE];
MWMappingDescriptor itemDescriptor = locateDescriptorFor(itemType);
// check the attributes of itemType for a backpointer
for (Iterator stream = itemType.attributes(); stream.hasNext(); ) {
MWClassAttribute attribute = (MWClassAttribute) stream.next();
if (attribute.getType() == sourceClass || (attribute.isValueHolder() && attribute.getValueType() == sourceClass)) {
MWMapping backMapping = itemDescriptor.mappingNamed(attribute.getName());
if (backMapping == null || attribute.getType().isAssignableToCollection()) {
continue;
}
if (backMapping instanceof MWOneToOneMapping) {
return (MWOneToOneMapping) backMapping;
}
return backMapping.asMWOneToOneMapping();
}
}
// if we can't find a backpointer in itemType, then look for a mapping in its descriptor
for (Iterator stream = itemDescriptor.mappings(); stream.hasNext(); ) {
MWMapping itemMapping = (MWMapping) stream.next();
if (itemMapping instanceof MWOneToOneMapping) {
MWOneToOneMapping oneToOne = (MWOneToOneMapping) itemMapping;
if (oneToOne.getReferenceDescriptor().getMWClass() == sourceClass) {
return oneToOne;
}
}
}
return null;
}
protected Collection copyInheritedPrimaryKeys( MWTableDescriptor superclassDesc, MWTableDescriptor subclassDesc ) {
// find key columns
MWTableDescriptor definingDescriptor = superclassDesc;
boolean definingDescriptorFound = false;
while (! definingDescriptorFound) {
// walk up class hierarchy looking for primary key
MWTableDescriptor superD = locateDescriptorFor( definingDescriptor.getMWClass().getSuperclass() );
if ( superD == null ) {
definingDescriptorFound = true;
} else {
definingDescriptor = superD;
}
}
if (!definingDescriptor.getInheritancePolicy().isActive()) {
definingDescriptor.addInheritancePolicy();
}
((MWDescriptorInheritancePolicy) definingDescriptor.getInheritancePolicy()).setIsRoot(true);
if (definingDescriptor.primaryKeysSize() == 0) {
logError(resourceRepository().getString("unableToFindKeyFieldsForInSuperclasses", new Object[] { subclassDesc.getMWClass().shortName() }));
ArrayList array = new ArrayList();
array.add(fabricateKeyFor( subclassDesc.getMWClass(), getTableFor(subclassDesc.getMWClass()) ) );
return array;
}
MWTable primaryTable = subclassDesc.getPrimaryTable();
for (Iterator stream = definingDescriptor.primaryKeys(); stream.hasNext(); ) {
MWColumn primaryKey = (MWColumn) stream.next();
if (! primaryTable.containsColumnNamed(primaryKey.getName())) {
((MWRelationalTransactionalPolicy) subclassDesc.getTransactionalPolicy()).getPrimaryKeyPolicy().addPrimaryKey(primaryTable.addColumnLike(primaryKey));
}
}
if (!subclassDesc.getInheritancePolicy().isActive()) {
subclassDesc.addInheritancePolicy();
}
MWDescriptorInheritancePolicy subInheritPolicy = (MWDescriptorInheritancePolicy) subclassDesc.getInheritancePolicy();
MWClassIndicatorPolicy subIndicatorPolicy = subInheritPolicy.getClassIndicatorPolicy();
subInheritPolicy.setIsRoot(false);
subInheritPolicy.setParentDescriptor( superclassDesc );
//MWInheritancePolicy defInheritPolicy = definingDescriptor.getInheritancePolicy();
//MWClassIndicatorPolicy defIndicatorPolicy = defInheritPolicy.getClassIndicatorPolicy();
// add indicator field and mapping to superclass descriptor
//String indicatorFieldName = defaultAbstractClassIndicatorFieldName();
//if (primaryTable.getFieldNamed( indicatorFieldName ) == null) {
// MWDatabasePlatform platform = getDatabase().getPlatform();
// MWDatabaseField indicatorField = createColumnNamedForJavaClassNamed( indicatorFieldName, "java.lang.String");
// primaryTable.addField( indicatorField );
// defIndicatorPolicy.setField( indicatorField);
// defIndicatorPolicy.addIndicator( definingDescriptor.getShortName(), definingDescriptor );
//}
//defIndicatorPolicy.addIndicator( subclassDesc.getShortName(), subclassDesc );
return null;
}
protected HashMap copyPrimaryKeyColumns( MWTable sourceTable, MWTable targetTable ) {
// Copy key columns from one table to another.
// The new columns are created as both primary keys and foreign keys."
// Returns a HashMap of sourceTable column names to the corresponding column name in the target table.
HashMap columnNameMap = new HashMap();
for (Iterator stream = sourceTable.primaryKeyColumns(); stream.hasNext(); ) {
MWColumn keyColumn = (MWColumn) stream.next();
MWColumn keyColumnCopy = targetTable.addColumnLike(keyColumn);
keyColumnCopy.setName(newColumnName( targetTable, sourceTable.getName() + "_" + keyColumn.getName()));
columnNameMap.put( keyColumn.getName(), keyColumnCopy.getName() );
keyColumnCopy.setPrimaryKey(false);
}
return columnNameMap;
}
protected MWColumn createColumn(String potentialName, MWTable sourceTable, MWColumn foreignColumn) {
String newColumnName = newColumnName(sourceTable, potentialName);
MWColumn newColumn = sourceTable.addColumn(newColumnName);
newColumn.setDatabaseType(foreignColumn.getDatabaseType());
return newColumn;
}
protected MWColumn createColumn(String name, String javaClassName, int arrayDepth, MWTable table) {
// this should only be called for database types.
// for collection variables, this will create a column for the item class."
String columnName = newColumnName(table, name);
DatabaseType dbType = null;
try {
dbType = getDatabase().getDatabasePlatform().databaseTypeForJavaTypeDeclaration(javaClassName, arrayDepth);
}catch (IllegalArgumentException iae){
// if (iae.getMessage().endsWith("java.lang.Object")) {
dbType = getDatabase().getDatabasePlatform().defaultDatabaseType();
// }
// else throw iae;
}
MWColumn column = table.addColumn(columnName);
column.setDatabaseType(dbType);
return column;
}
protected MWColumn createPrimaryKeyColumn(String fieldName, String javaClassName, int arrayDepth, MWTable table) {
MWColumn column = createColumn(fieldName, javaClassName, arrayDepth, table);
column.setPrimaryKey(true);
return column;
}
protected void createPrimaryKeyFields(MWClass bldrClass, MWTable table) {
Collection keyColumns = new ArrayList();
// MWTableDescriptor subclassDescriptor = getDescriptorFor(bldrClass);
// check my superclass for key fields
MWTableDescriptor superclassDescriptor = locateDescriptorFor(bldrClass.getSuperclass());
if (superclassDescriptor != null) {
Collection parentKeys = copyInheritedPrimaryKeys(superclassDescriptor, getDescriptorFor(bldrClass));
if (parentKeys == null) {
return;
}
}
// try to guess the primary key
if (keyColumns.isEmpty()) {
keyColumns.addAll(defaultKeysFor(bldrClass, table));
}
// if still empty, then we need to create a primary key
if (keyColumns.isEmpty()) {
// default name not found
keyColumns.add(fabricateKeyFor(bldrClass, table));
}
for (Iterator stream = keyColumns.iterator(); stream.hasNext(); ) {
Object[] varAndColumn = (Object[]) stream.next();
MWClassAttribute attribute = (MWClassAttribute) varAndColumn[0];
MWColumn column = (MWColumn) varAndColumn[1];
MWTableDescriptor descriptor = getDescriptorFor(bldrClass);
MWMapping existingMapping = descriptor.mappingForAttribute(attribute);
if (existingMapping != null) {
descriptor.removeMapping(existingMapping);
}
MWDirectToFieldMapping mapping = (MWDirectToFieldMapping) descriptor.addDirectMapping(attribute);
mapping.setColumn(column);
mapping.setUsesMethodAccessing(getProject().getDefaultsPolicy().isMethodAccessing());
((MWRelationalTransactionalPolicy) descriptor.getTransactionalPolicy()).getPrimaryKeyPolicy().addPrimaryKey(column);
}
}
protected MWReference createReferenceIfAbsent(MWTable sourceTable, MWTable targetTable, HashMap keyColumnTranslations) {
String nameOfReference = sourceTable.getName() + "_" + targetTable.getName();
MWReference reference = sourceTable.referenceNamed(nameOfReference);
if (reference != null) {
return reference;
}
reference = sourceTable.addReference(nameOfReference, targetTable);
// populate the field associations in the reference
for (Iterator stream = targetTable.primaryKeyColumns(); stream.hasNext(); ) {
MWColumn targetColumn = (MWColumn) stream.next();
String sourceColumnName = targetColumn.getName();
if (keyColumnTranslations != null && keyColumnTranslations.get(targetColumn.getName()) != null) {
sourceColumnName = (String) keyColumnTranslations.get(targetColumn.getName());
}
MWColumn joinColumn = sourceTable.columnNamed(sourceColumnName);
reference.addColumnPair(joinColumn, targetColumn);
}
return reference;
}
protected MWTable createTableIfAbsent(MWTableDescriptor descriptor) {
// returnsWith: <BLDRDatabaseTable> - find the existing table or create a new one"
MWClass bldrClass = descriptor.getMWClass();
if (bldrClass.attributesSize() == 0 && descriptor.mappingsSize() == 0) {
if(bldrClass.getSuperclass() != null) {
MWTableDescriptor parentDescriptor = getDescriptorFor(bldrClass.getSuperclass());
if (parentDescriptor != null)
descriptor.setPrimaryTable(parentDescriptor.getPrimaryTable());
}
return null;
}
MWTable table = descriptor.getPrimaryTable();
if (table == null) {
String proposedName;
proposedName = bldrClass.defaultTableNameWithLength(getTableNameLength());
table = getDatabase().addTable(newTableName(proposedName));
this.tableCreatedCount++;
descriptor.setPrimaryTable(table);
}
this.tableLookup.put(bldrClass, table);
// generate primary keys, if absent
if (!table.primaryKeyColumns().hasNext()) {
createPrimaryKeyFields(bldrClass, table);
}
return table;
}
public static String defaultAbstractClassIndicatorFieldName() {
return "SUBCLASS";
}
protected Collection defaultKeysFor(MWClass bldrClass, MWTable table) {
String pkSearchPattern = getProject().getTableGenerationPolicy().getPrimaryKeySearchPattern();
Collection result = new ArrayList();
for (Iterator stream = bldrClass.attributes(); stream.hasNext(); ) {
MWClassAttribute attribute = (MWClassAttribute) stream.next();
if (new SimpleStringMatcher(pkSearchPattern, true).matches(attribute.getName())) {
MWColumn column = createPrimaryKeyColumn(attribute.getName(), attribute.typeName(), attribute.getDimensionality(), table);
if (column != null) {
result.add( new Object[] { attribute, column } );
}
}
}
//try superclass attributes, do this after going through the non-inherited attributes so that they will
//be resolved first. fix for bug #4008198
if (result.isEmpty()) {
for (Iterator stream = bldrClass.allAttributes(); stream.hasNext(); ) {
MWClassAttribute attribute = (MWClassAttribute) stream.next();
if (new SimpleStringMatcher(pkSearchPattern, true).matches(attribute.getName())) {
getDescriptorFor(bldrClass).addInheritedAttribute(attribute);
MWColumn column = createPrimaryKeyColumn(attribute.getName(), attribute.typeName(), attribute.getDimensionality(), table);
if (column != null) {
result.add( new Object[] { attribute, column } );
}
}
}
}
return result;
}
/** Determine the type of the attribute and it's itemType, if it is a collection.
Try to get as much information from the attribute, but if that is incomplete,
then get the rest from the mapping. ValueHolders are a special case of this
because the attribute can't know the itemType for an indirect collection that
is using a ValueHolder. (This is because the attrType is ValueHolder and
itemType is just a collection, so the attribute doesn't know what's in the
collection.)
Anyway, we hope that the user has already chosen an appropriate
mapping and we can capitalize on this.
**/
protected MWClass[] determineAttrAndItemType(MWMapping mapping, boolean firstPass) {
MWClassAttribute attribute = mapping.getInstanceVariable();
if (attribute == null) {
return new MWClass[] { null, null };
}
MWClass attrType = null;
MWClass itemType = null;
// if the attribute uses a ValueHolder, then attribute.getType()
// will return a VH. That's not useful to us.
if (attribute.isValueHolder()) {
//value type has been set, return that because its useful
if (!attribute.getValueType().isObject()) {
attrType = attribute.getValueType();
if (attrType.isAssignableToCollection()) {
itemType = attribute.getItemType();
}
//it has not been set log message
} else {
if (firstPass) {
logUrgent(resourceRepository().getString("VALUE_HOLDER_TYPE_SELECT", new Object[] {attribute.getName(), attribute.getDeclaringType().getName() }));
}
attrType = attribute.getType();
}
} else {
attrType = attribute.getType();
itemType = attribute.getItemType();
}
// if we are missing information, then see if the mapping can help
if (attrType == null || itemType == null) {
// MWCollectionMapping
if (mapping != null) {
if (mapping.isCollectionMapping()) {
MWCollectionMapping collectionMapping = (MWCollectionMapping) mapping;
if (attrType == null) {
attrType = collectionMapping.getContainerPolicy().getDefaultingContainerClass().getContainerClass();
}
if (itemType == null && collectionMapping.getReferenceDescriptor() != null) {
itemType = collectionMapping.getReferenceDescriptor().getMWClass();
}
}
// MWReferenceMapping
else if (mapping instanceof MWReferenceMapping) {
MWReferenceMapping referenceMapping = (MWReferenceMapping) mapping;
if (attrType == null && referenceMapping.getReferenceDescriptor() != null) {
attrType = referenceMapping.getReferenceDescriptor().getMWClass();
}
}
}
}
return new MWClass[] { attrType, itemType };
}
protected static boolean directCollectionContentsAreSorted() {
// There are really three options for direct collections: either
// 1. their contents are sorted implying that their position in the array can be recalculated,
// 2. their contents are unsorted, but position-dependent
// 3. their contents are unsorted, but position-independent.
// If the contents are unsorted, it is harmless to retiain the original order (by saving each item's index)."
return true;
}
protected static boolean directCollectionContentsAreUnsorted() {
// There are really three options for direct collections: either
// 1. their contents are sorted implying that their position in the array can be recalculated,
// 2. their contents are unsorted, but position-dependent
// 3. their contents are unsorted, but position-independent.
// If the contents are unsorted, it is harmless to retiain the original order (by saving each item's index)."
return ! directCollectionContentsAreSorted();
}
protected static boolean directCollectionsHaveUniqueEntries() {
// Tables defined to store direct collections of TOPLink-ignorant classes will require more primary keys.
// If the collection has unique entries, then the entry field can be the other primary key.
// If not, the easiest hack is to create a column for each entry's index."
return true;
}
protected Object[] fabricateKeyFor( MWClass bldrClass, MWTable table ) {
// returnsWith: <Association key: <BLDRVariableDefinition> value: <BLDRDatabaseField>>
// class - <BLDRClassDefinition> does not have an obvious key variable, so create one
StringBuffer buffer = new StringBuffer();
buffer.append(resourceRepository().getString("noKeyFieldFound", new Object[] { bldrClass.shortName() }));
buffer.append(System.getProperty("line.separator"));
buffer.append(resourceRepository().getString("pleaseEditJavaSourceFileAppropriately"));
logUrgent(buffer.toString());
String defaultPKName = getProject().getTableGenerationPolicy().getDefaultPrimaryKeyName().toLowerCase();
MWClass typeInt = getProject().typeFor(int.class);
MWClassAttribute attribute = bldrClass.addAttribute(defaultPKName, typeInt);
MWColumn primaryKey = createPrimaryKeyColumn( attribute.getName(), attribute.typeName(), attribute.getDimensionality(), table );
return new Object[] {attribute, primaryKey };
}
protected MWMapping generateForMWAggregateMapping( MWMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
MWAggregateMapping aggregateMapping;
if (mapping instanceof MWAggregateMapping) {
aggregateMapping = (MWAggregateMapping) mapping;
} else {
aggregateMapping = mapping.asMWAggregateMapping();
}
boolean mapCollectionMappings = false;
mapAggregateMapping( aggregateMapping, attrAndItemType, sourceClass, mapCollectionMappings );
return mapping;
}
protected MWCollectionMapping generateForMWCollectionMapping( MWMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
MWClass itemType = attrAndItemType[this.ITEM_TYPE];
if (itemType.isInterface() || itemType.getName().equals(java.lang.Object.class.getName())) {
return null;
}
MWCollectionMapping collectionMapping;
// if the mapping is already a collection mapping, then respect its decision.
if (mapping != null && mapping.isCollectionMapping()) {
collectionMapping = (MWCollectionMapping) mapping;
} else {
// check for singular backpointer
MWOneToOneMapping backpointer = backpointerFor( mapping, attrAndItemType, sourceClass );
if (backpointer == null) {
// either there is no back pointer, a collection back pointer or we can't add a back pointer"
logNotify(resourceRepository().getString("refersToWhichHasNoBackpointer", new Object[] { sourceClass.shortName(), mapping.getName(), itemType.shortName() }));
collectionMapping = mapping.asMWManyToManyMapping();
} else {
logAssumption(resourceRepository().getString("isBackpointerFor", new Object[] { itemType.shortName(), backpointer.getName(), sourceClass.shortName(), mapping.getName() }));
collectionMapping = mapping.asMWOneToManyMapping();
}
}
if (collectionMapping instanceof MWManyToManyMapping) {
mapManyToManyMapping((MWManyToManyMapping)collectionMapping, attrAndItemType, sourceClass);
} else {
mapOneToManyMapping((MWOneToManyMapping)collectionMapping, attrAndItemType, sourceClass);
}
collectionMapping.getContainerPolicy().getDefaultingContainerClass().setContainerClass(attrAndItemType[this.ATTR_TYPE]);
collectionMapping.setReferenceDescriptor(locateDescriptorFor(itemType));
return collectionMapping;
}
protected MWMapping generateForMWDirectCollectionMapping( MWMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
MWRelationalDirectCollectionMapping collectionMapping;
if (mapping instanceof MWRelationalDirectCollectionMapping) {
collectionMapping = (MWRelationalDirectCollectionMapping) mapping;
} else {
collectionMapping = (MWRelationalDirectCollectionMapping) mapping.asMWDirectCollectionMapping();
}
mapDirectCollectionMapping( collectionMapping, attrAndItemType, sourceClass );
return collectionMapping;
}
protected MWMapping generateForMWDirectToFieldMapping( MWMapping mapping, MWClass sourceClass ) {
MWDirectToFieldMapping dtfMapping;
if (mapping instanceof MWDirectToFieldMapping) {
dtfMapping = (MWDirectToFieldMapping) mapping;
} else {
dtfMapping = (MWDirectToFieldMapping) mapping.asMWDirectMapping();
}
mapDirectToFieldMapping(dtfMapping, sourceClass);
return dtfMapping;
}
protected MWMapping generateForMWOneToOneMapping( MWMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
MWOneToOneMapping oneToOneMapping;
if (mapping instanceof MWOneToOneMapping) {
oneToOneMapping = (MWOneToOneMapping) mapping;
} else {
oneToOneMapping = mapping.asMWOneToOneMapping();
}
mapOneToOneMapping( oneToOneMapping, attrAndItemType, sourceClass );
return oneToOneMapping;
}
protected MWMapping generateForCollectionMapping(MWMapping mapping, MWClass sourceClass) {
// already associated with a table
Collection writtenFields = new ArrayList();
mapping.addWrittenFieldsTo(writtenFields);
if (! ((MWRelationalDescriptor) mapping.getParentDescriptor()).isAggregateDescriptor() && writtenFields.size() > 0) {
return mapping;
}
MWClass[] attrAndItemType = determineAttrAndItemType( mapping, false );
MWClass attrType = attrAndItemType[this.ATTR_TYPE];
MWClass itemType = attrAndItemType[this.ITEM_TYPE];
// if the mapping is a DirectCollection, we can make some guesses about missing information.
if (mapping instanceof MWDirectCollectionMapping) {
if (attrType == null) {
attrType = attrAndItemType[this.ATTR_TYPE] = getProject().typeFor(java.util.Vector.class);
}
if (itemType == null) {
itemType = attrAndItemType[this.ITEM_TYPE] = getProject().typeFor(java.lang.String.class);
}
}
// return null if we have incomplete information
if (attrType == null) {
return null;
}
// Aggregate
if (mapping instanceof MWAggregateMapping) {
boolean mapCollectionMappings = true;
mapAggregateMapping( (MWAggregateMapping) mapping, attrAndItemType, sourceClass, mapCollectionMappings );
return mapping;
}
// return null, if we have incomplete information
// not sure what to do with interfaces.
if (itemType == null || itemType.isInterface()) {
return null;
}
// Collection
if (attrType.isAssignableToCollection()) {
// DirectCollection
if (isDatabaseType(itemType)) {
return generateForMWDirectCollectionMapping( mapping, attrAndItemType, sourceClass );
}
// BldrCollection
else {
return generateForMWCollectionMapping( mapping, attrAndItemType, sourceClass );
}
}
return null;
}
protected MWMapping generateForNonCollectionMapping(MWMapping mapping, MWClass sourceClass) {
// already associated with a table
Collection writtenFields = new ArrayList();
mapping.addWrittenFieldsTo(writtenFields);
if (! ((MWRelationalDescriptor) mapping.getParentDescriptor()).isAggregateDescriptor() && writtenFields.size() > 0) {
return mapping;
}
MWClass[] attrAndItemType = determineAttrAndItemType( mapping, true );
MWClass attrType = attrAndItemType[this.ATTR_TYPE];
// return null, if we have incomplete information
if (attrType == null) {
return null;
}
// Collections are handled in the next pass.
// not sure what to do with interfaces.
if (attrType.isAssignableToCollection() || attrType.isInterface()) {
return null;
}
// DirectMapping
if (isDatabaseType(attrType)) {
return generateForMWDirectToFieldMapping( mapping, sourceClass );
}
MWMappingDescriptor targetDescriptor = (MWMappingDescriptor) getProject().descriptorForType(attrType);
//incomplete mapping
if (targetDescriptor== null) {
return null;
}
boolean targetDescriptorIsAggregate = ((MWRelationalDescriptor) targetDescriptor).isAggregateDescriptor();
if (mapping instanceof MWAggregateMapping|| targetDescriptorIsAggregate ) {
// Aggregate
return generateForMWAggregateMapping( mapping, attrAndItemType, sourceClass );
}
// OneToOne
else {
return generateForMWOneToOneMapping( mapping, attrAndItemType, sourceClass );
}
}
protected static boolean generateIndexColumnsForDirectCollections() {
// There are really three options for direct collections: either
// 1. their contents are sorted implying that their position in the array can be recalculated,
// 2. their contents are unsorted, but position-dependent
// 3. their contents are unsorted, but position-independent.
// If the contents are unsorted, it is harmless to retiain the original order (by saving each item's index)."
return directCollectionContentsAreUnsorted();
}
protected Collection getClassDescriptors() {
return this.classDescriptors;
}
protected int getColumnNameLength() {
//return getProject().getDatabase().getPlatform().getMaxColumnNameLength();
return 30;
}
protected MWDatabase getDatabase() {
return getProject().getDatabase();
}
protected MWTableDescriptor getDescriptorFor(MWClass bldrClass) {
return (MWTableDescriptor) this.descriptorLookup.get(bldrClass);
}
protected static HashMap getPrimitiveToJavaTypeMap() {
if (primitiveToJavaTypeMap == null) {
initializePrimitiveToJavaTypeMap();
}
return primitiveToJavaTypeMap;
}
protected MWRelationalProject getProject() {
return this.project;
}
protected MWTable getRelationshipTableFor(MWClass aClass, MWClass anotherClass) {
MWTable table = null;
// classCollection is a collection of collections that contain bldrClasses
for (Iterator stream = this.relationshipTableLookup.keySet().iterator(); stream.hasNext(); ) {
Collection classes = (Collection) stream.next();
if (classes.contains(aClass) && classes.contains(anotherClass)) {
table = (MWTable) this.relationshipTableLookup.get(classes);
return table;
}
}
return null;
}
protected MWTable getTableFor(MWClass bldrClass) {
return (MWTable) this.tableLookup.get(bldrClass);
}
protected int getTableNameLength() {
// ^self project databaseInterface maxTableNameLength
return 30;
}
protected static void initializePrimitiveToJavaTypeMap() {
primitiveToJavaTypeMap = new HashMap();
primitiveToJavaTypeMap.put("byte", "java.lang.Byte");
primitiveToJavaTypeMap.put("short", "java.lang.Short");
primitiveToJavaTypeMap.put("int", "java.lang.Integer");
primitiveToJavaTypeMap.put("char", "java.lang.Character");
primitiveToJavaTypeMap.put("long", "java.lang.Long");
primitiveToJavaTypeMap.put("double", "java.lang.Double");
primitiveToJavaTypeMap.put("float", "java.lang.Float");
primitiveToJavaTypeMap.put("boolean", "java.lang.Boolean");
}
protected boolean isByteArray(MWClassAttribute attribute) {
return attribute.getType().isBytePrimitive() && attribute.getDimensionality() == 1;
}
protected boolean isDatabaseType(MWClass type) {
if (type == null) {
return false;
}
return getDatabase().getDatabasePlatform().javaTypeDeclarationCanBeMappedToDatabaseType(type.getName(), 0);
}
protected MWTableDescriptor locateDescriptorFor(MWClass bldrClass) {
// returnsWith: <BLDRDescriptorDefinition>
// this returns the descriptor required, even if it is not associated with a selected class"
if (bldrClass == null) {
return null;
}
MWTableDescriptor descriptor = getDescriptorFor(bldrClass);
if (descriptor != null) {
return descriptor;
} else {
return (MWTableDescriptor) getProject().descriptorForType(bldrClass);
}
}
protected void logAssumption(String errorMessage) {
this.log.add(new MWError("TABLE_GENERATOR_ASSUMPTION", errorMessage));
}
protected void logError(String errorMessage) {
this.log.add(new MWError("TABLE_GENERATOR_ERROR", errorMessage));
}
protected void logNotify(String errorMessage) {
this.log.add(new MWError("TABLE_GENERATOR_WARNING", errorMessage));
}
protected void logUrgent(String errorMessage) {
this.log.add(new MWError("TABLE_GENERATOR_URGENT", errorMessage));
}
protected void mapAggregateMapping( MWAggregateMapping aggMapping, MWClass[] attrAndItemType, MWClass sourceClass, boolean mapCollectionMappings ) {
MWMappingDescriptor descriptor = (MWMappingDescriptor) aggMapping.getReferenceDescriptor();
if (descriptor == null) {
descriptor = (MWMappingDescriptor) getProject().descriptorForType(attrAndItemType[this.ATTR_TYPE]);
}
// we generate the collection mappings in Pass 3 and the rest in Pass 2.
for (Iterator stream = descriptor.mappings(); stream.hasNext(); ) {
MWMapping mapping = (MWMapping) stream.next();
if (mapCollectionMappings) {
generateForCollectionMapping( mapping, sourceClass );
} else {
generateForNonCollectionMapping( mapping, sourceClass );
}
}
}
protected void mapDirectCollectionMapping(MWRelationalDirectCollectionMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass) {
MWClassAttribute attribute = mapping.getInstanceVariable();
MWClass itemClass = attrAndItemType[this.ITEM_TYPE];
MWClass attrClass = attrAndItemType[this.ATTR_TYPE];
String assumptions = new String();
int length = (getTableNameLength() / 2) - 1;
String proposedName = sourceClass.defaultTableNameWithLength(length) + "_TO_" + itemClass.defaultTableNameWithLength(length);
MWTable table = getDatabase().addTable(newTableName(proposedName));
HashMap columnNames = copyPrimaryKeyColumns(getTableFor(sourceClass), table);
MWColumn column = createColumn(attribute.getName(), itemClass.getName(), 0, table);
if (directCollectionsHaveUniqueEntries()) {
assumptions = resourceRepository().getString("isUnique", new Object[] { assumptions });
} else {
// add index column and make it a key field, or use a sequence"
}
// if (generateIndexColumnsForDirectCollections()) {
// assumptions = assumptions + "@@junsorted ";
// table.addField( createColumnNamedForJavaClassNamed( "INDEX", "int") );
// } else {
// assumptions = assumptions + "@@jsorted ";
// }
logAssumption(resourceRepository().getString("directCollectionHasEntries", new Object[]{ sourceClass.shortName(), attribute.getName(), assumptions }));
mapping.setDirectValueColumn(column);
mapping.getContainerPolicy().getDefaultingContainerClass().setContainerClass(attrClass);
mapping.setUsesMethodAccessing(getProject().getDefaultsPolicy().isMethodAccessing());
MWReference reference = createReferenceIfAbsent(table, getTableFor(sourceClass), columnNames);
mapping.setReference(reference);
}
protected void mapDirectToFieldMapping(MWDirectToFieldMapping mapping, MWClass sourceClass) {
MWClassAttribute attribute = mapping.getInstanceVariable();
MWTable table = getTableFor(sourceClass);
MWColumn column = createColumn(attribute.getName(), attribute.typeName(), 0, table);
if (! ((MWRelationalDescriptor) mapping.getParentDescriptor()).isAggregateDescriptor()) {
mapping.setColumn(column);
}
}
protected void mapManyToManyMapping( MWManyToManyMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
// get table for the source object
MWTable sourceTable = getTableFor(sourceClass);
if (sourceTable == null) {
return;
}
// get descriptor and table for the target items
MWClass itemClass = attrAndItemType[this.ITEM_TYPE];
MWTableDescriptor itemDescriptor = locateDescriptorFor(itemClass);
if (itemDescriptor == null) {
logError(resourceRepository().getString("couldNotFindMapping", new Object[]{ mapping.getName(), sourceClass.shortName(), itemClass.getName()}));
return;
}
MWTable itemTable = itemDescriptor.getPrimaryTable();
if (itemTable == null) {
logError(resourceRepository().getString("descriptorDoesNotHaveAPrimaryTable", new Object[]{ itemDescriptor.getMWClass().shortName() }));
return;
}
HashMap sourceKeyNames = null;
HashMap itemKeyNames = null;
// get the join table if it exists
MWTable joinTable = getRelationshipTableFor( sourceClass, itemClass );
// if null, create a new joinTable
if (joinTable == null) {
// create the joinTable
int length = (getTableNameLength() / 2) - 1;
String proposedName = sourceClass.defaultTableNameWithLength(length) + "_TO_" +
itemClass.defaultTableNameWithLength(length);
joinTable = getDatabase().addTable(newTableName( proposedName ));
// copy the keys from the source and target tables into the joinTable
sourceKeyNames = copyPrimaryKeyColumns( sourceTable, joinTable );
itemKeyNames = copyPrimaryKeyColumns( itemTable, joinTable );
// add the join table to the database
registerRelationshipTable( joinTable, sourceClass, itemClass );
}
mapping.setRelationTable(joinTable);
// create a reference from the joinTable to the sourceTable
String nameOfReference = joinTable.getName() + "_" + sourceTable.getName();
MWReference sourceReference = createReferenceIfAbsent( joinTable, sourceTable, sourceKeyNames );
mapping.setSourceReference(sourceReference);
// create a reference from the joinTable to the targetTable
nameOfReference = joinTable.getName() + "_" + itemTable.getName();
MWReference targetReference = createReferenceIfAbsent( joinTable, itemTable, itemKeyNames );
mapping.setTargetReference(targetReference);
mapping.setReferenceDescriptor(itemDescriptor);
}
protected void mapOneToManyMapping( MWOneToManyMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
MWClass itemClass = attrAndItemType[this.ITEM_TYPE];
if (itemClass.isInterface()) {
return;
}
MWTableDescriptor itemDescriptor = locateDescriptorFor(itemClass);
if (itemDescriptor.getPrimaryTable() == null) {
logError(resourceRepository().getString("couldNotFindMapping", new Object[]{ mapping.getName(), sourceClass.shortName(), itemClass.getName() }));
return;
}
MWOneToOneMapping backMapping = backpointerFor( mapping, attrAndItemType, sourceClass );
if (backMapping == null) {
logUrgent(resourceRepository().getString("thereIsNoOneToOneBackpointer", new Object[] { mapping.getName(), sourceClass.shortName() }));
return;
}
// if a reference doesn't exist, then create it
MWReference reference = backMapping.getReference();
if (reference == null || reference.columnPairsSize() == 0) {
logUrgent(resourceRepository().getString("errorMapOneToManyMapping"));
return;
}
mapping.setReference(backMapping.getReference());
mapping.setReferenceDescriptor( itemDescriptor );
}
protected void mapOneToOneMapping( MWOneToOneMapping mapping, MWClass[] attrAndItemType, MWClass sourceClass ) {
MWTableDescriptor sourceDescriptor = getDescriptorFor( sourceClass );
MWTable sourceTable = getTableFor( sourceClass );
// ask project because we may not be modifying this descriptor"
MWClass foreignClass = attrAndItemType[this.ATTR_TYPE];
MWTableDescriptor targetDescriptor = (MWTableDescriptor) getProject().descriptorForType(foreignClass);
if (targetDescriptor == null) {
logError(resourceRepository().getString("couldNotFindMapping", new Object[] { mapping.getName(), sourceDescriptor.getName(), foreignClass.getName() }));
return;
}
mapping.setUsesMethodAccessing(getProject().getDefaultsPolicy().isMethodAccessing());
MWTable targetTable = targetDescriptor.getPrimaryTable();
// create associations for each primary key"
if (targetTable != null
&& targetTable.columnsSize() > 0) {
String referenceName = sourceTable.getName() + "_" + targetTable.getName();
MWReference reference = sourceTable.referenceNamed(referenceName);
if (reference == null) {
reference = sourceTable.addReference(referenceName, targetTable);
}
for (Iterator stream = targetTable.primaryKeyColumns(); stream.hasNext(); ) {
MWColumn foreignColumn = (MWColumn) stream.next();
// create column
MWColumn column = createColumn(mapping.getName(), sourceTable, foreignColumn);
// add key pair
reference.addColumnPair(column, foreignColumn);
}
mapping.setReference(reference);
}
mapping.setReferenceDescriptor(targetDescriptor);
}
protected String mungeName(String aString) {
// munge the string by slapping an integer on the end, do not adjust the length of the string"
if (aString == null || aString == "") {
return "1";
}
// if the last character is not a digit
if (! Character.isDigit( aString.charAt(aString.length() - 1) )) {
return aString + "1";
}
// we know that the string ends with a number
int startOfNumber = aString.length() - 1;
for (; startOfNumber >= 0; startOfNumber--) {
// if it's not a digit
if (! Character.isDigit(aString.charAt(startOfNumber))) {
startOfNumber++;
break;
}
}
String oldNumberString = aString.substring(startOfNumber);
int number = Integer.parseInt(oldNumberString);
String newNumberString = String.valueOf(number + 1);
//BUG# 2758136 - changed startOfNumber -1 to startOfNumber
//With aggregates, field names were being generated like this:
//HOURS, HOURS1, HOUR2, HOU3
return aString.substring(0, startOfNumber) + newNumberString;
}
protected String newColumnName(MWTable aTable, String proposedString) {
// check for column name collisions"
String newString = proposedString.replace('$', '_');
String columnName = StringTools.convertCamelBackToAllCaps(newString, getColumnNameLength());
while (aTable.columnNamed(columnName) != null) {
columnName = mungeName(columnName);
}
return columnName;
}
protected String newTableName(String proposedString) {
// check for table name collisions"
String tableName = proposedString.replace('$', '_');
while (this.getDatabase().containsTableNamed(tableName)) {
tableName = mungeName( tableName );
}
return tableName;
}
protected void registerRelationshipTable(MWTable aTable, MWClass aClass, MWClass anotherClass) {
ArrayList classes = new ArrayList();
classes.add(aClass);
classes.add(anotherClass);
this.relationshipTableLookup.put( classes, aTable );
}
protected void setClassDescriptors(Collection classDescriptors) {
this.classDescriptors = classDescriptors;
}
protected void setProject(MWRelationalProject project) {
this.project = project;
}
protected Collection sortTableDescriptors(Collection aCollection) {
// returnsWith: <OrderedCollection elements: <BLDRClassDefinition>>
// aCollection - <Collection elements: <BLDRDescriptorDefinition>>"
// The list of descriptors is sorted so that when creating a table, the table for its superclass
// has already been created."
Collection orderedList = new ArrayList();
// only MWTableDescriptors
Collection nonClassDescriptors = new ArrayList();
for (Iterator stream = aCollection.iterator(); stream.hasNext(); ) {
Object desc = stream.next();
if (! (desc instanceof MWTableDescriptor))
nonClassDescriptors.add(desc);
}
aCollection.removeAll(nonClassDescriptors);
// this is a map from a bldrClass to an ArrayList that contains the
// superclass of the bldrClass
HashMap dependencyList = new HashMap();
int count = 0;
// iterate overall the descriptors and determine the superclass
// of the MWClass associated with each descriptor.
for (Iterator descriptors = aCollection.iterator(); descriptors.hasNext(); ) {
count++;
MWTableDescriptor descriptor = (MWTableDescriptor) descriptors.next();
MWClass bldrClass = descriptor.getMWClass();
this.descriptorLookup.put( bldrClass, descriptor);
MWTableDescriptor foundDependency = null;
for (Iterator descriptors2 = aCollection.iterator(); descriptors2.hasNext(); ) {
MWTableDescriptor descriptor2 = (MWTableDescriptor) descriptors2.next();
if (descriptor2.getMWClass() == bldrClass.getSuperclass()) {
foundDependency = descriptor2;
break;
}
}
ArrayList dependsOn = new ArrayList(1);
if (foundDependency != null) {
dependsOn.add(bldrClass.getSuperclass());
}
dependencyList.put(bldrClass, dependsOn);
}
// iterate over the dependencyList, removing bldrClasses that whose superclass
// has already been removed and putting them in the orderedList.
while (!dependencyList.isEmpty()) {
MWClass current = null;
for (Iterator stream = dependencyList.keySet().iterator(); stream.hasNext(); ) {
MWClass bldrClass = (MWClass) stream.next();
ArrayList dependsOn = (ArrayList) dependencyList.get(bldrClass);
if (dependsOn.isEmpty()) {
current = bldrClass;
}
}
if (current == null) {
throw new RuntimeException(resourceRepository().getString("errorCyclicDependency"));
}
MWTableDescriptor descriptor = getDescriptorFor(current);
orderedList.add(descriptor);
dependencyList.remove(current);
for (Iterator stream = dependencyList.keySet().iterator(); stream.hasNext(); ) {
MWClass bldrClass = (MWClass) stream.next();
ArrayList dependsOn = (ArrayList) dependencyList.get(bldrClass);
dependsOn.remove(current);
}
}
return orderedList;
}
private ResourceRepository resourceRepository() {
return this.workbenchContext.getApplicationContext().getResourceRepository();
}
}