/******************************************************************************* * 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.mappingsmodel.generation; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWColumn; import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWColumnPair; 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.InterfaceDescriptorCreationException; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWMappingDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWTransactionalDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWTypeNames; 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.relational.MWAbstractTableReferenceMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWDirectToFieldMapping; 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.MWTableReferenceMapping; 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.utility.ClassTools; import org.eclipse.persistence.tools.workbench.utility.CollectionTools; import org.eclipse.persistence.tools.workbench.utility.NameTools; import org.eclipse.persistence.tools.workbench.utility.string.StringTools; /** * Generation of descriptors from tables. */ public class MWDescriptorGenerator implements MWTypeNames { // instance variables to be set by the user of this class private volatile MWRelationalProject project; private Collection tables; private volatile String packageName; private volatile boolean generateBidirectionalRelationships; private volatile boolean generateEjbs; private volatile boolean generateLocalInterfaces; // should only be true if generateEjbs private volatile boolean generateRemoteInterfaces; // should only be true if generateEjbs private volatile boolean generateMethodAccessors; // should always be true if generateEjbs private Collection relationshipsToCreate; // must be MWRelationshipHolders between tables contained in "tables" // used internally private Map tableToDescriptorDictionary; private Map relationshipToMappingDictionary; public MWDescriptorGenerator() { super(); this.relationshipsToCreate = new Vector(); this.tableToDescriptorDictionary = new HashMap(); this.relationshipToMappingDictionary = new HashMap(); } // Generate an attribute name. Don't bother making it unique yet. private String defaultInstanceVariableName(MWRelationshipHolder relationshipHolder, MWClass sourceClass, String targetClassShortName) { // try to get the name from the reference, if it has just one field if (! relationshipHolder.isForeignKeyInTargetTable() && relationshipHolder.getReference().columnPairsSize() == 1) { String columnName = ((MWColumnPair) relationshipHolder.getReference().columnPairs().next()).getSourceColumn().getName(); columnName = columnName.replace(' ', '_'); if (columnName.toUpperCase().endsWith("_ID")) { columnName = columnName.substring(0, columnName.length() - 3); } return StringTools.convertAllCapsToCamelBack(columnName, false); } return StringTools.uncapitalize(targetClassShortName); } // make sure that back pointers are created for 1-M's private void ensureBackPointersIncluded() { Vector backPointersToCreate = new Vector(); for (Iterator relIt = this.relationshipsToCreate.iterator(); relIt.hasNext(); ) { MWRelationshipHolder relHolder = (MWRelationshipHolder) relIt.next(); if (relHolder.isOneToMany()) { MWReference ref = relHolder.getReference(); boolean foreignKeyInTargetTable = false; if (getRelationshipToCreate(ref, foreignKeyInTargetTable) == null) { MWRelationshipHolder backPointer = new MWRelationshipHolder(ref, foreignKeyInTargetTable); backPointer.setOneToOne(); backPointersToCreate.add(backPointer); } } } this.relationshipsToCreate.addAll(backPointersToCreate); } private void ensureSpecCompliance() { for (Iterator descriptors = this.tableToDescriptorDictionary.values().iterator(); descriptors.hasNext(); ) { MWDescriptor desc = (MWDescriptor) descriptors.next(); } } private void generateBidirectionalRelationships() { for (Iterator relationships = this.relationshipsToCreate.iterator(); relationships.hasNext(); ) { MWRelationshipHolder relationship = (MWRelationshipHolder) relationships.next(); MWRelationshipHolder relationshipPartner = getRelationshipToCreate(relationship.getReference(), ! relationship.isForeignKeyInTargetTable()); MWAbstractTableReferenceMapping tableRefMapping = (MWAbstractTableReferenceMapping) this.relationshipToMappingDictionary.get(relationship); MWAbstractTableReferenceMapping tableRefMappingPartner = (MWAbstractTableReferenceMapping) this.relationshipToMappingDictionary.get(relationshipPartner); if (tableRefMappingPartner != null) { tableRefMapping.setMaintainsBidirectionalRelationship(true); tableRefMapping.setRelationshipPartnerMapping(tableRefMappingPartner); tableRefMappingPartner.setMaintainsBidirectionalRelationship(true); tableRefMappingPartner.setRelationshipPartnerMapping(tableRefMapping); } } } public void generateClassesAndDescriptors() { ensureBackPointersIncluded(); // makes sure that 1-M relationships have backpointer relationships for (Iterator stream = this.tables.iterator(); stream.hasNext(); ) { MWTable table = (MWTable) stream.next(); String typeName = table.getShortName(); typeName = typeName.replace(' ', '_'); typeName = StringTools.convertAllCapsToCamelBack(typeName); String ejbName = ClassTools.shortNameForClassNamed(typeName); if (this.packageName != null && this.packageName.length() != 0) { typeName = this.packageName + '.' + typeName; } if (this.getProject().getRepository().typeNamedIgnoreCase(typeName) != null) { typeName = generateUniqueCaseInsensitiveName(typeName); } if (this.generateEjbs) { typeName = typeName + "EJB"; } MWClass type = this.getProject().typeNamed(typeName); type.clear(); MWTableDescriptor descriptor; MWDescriptor existingDescriptor = this.getProject().descriptorForType(type); if (existingDescriptor != null) { getProject().removeDescriptor(existingDescriptor); } type.addZeroArgumentConstructor(); try { descriptor = (MWTableDescriptor) this.getProject().addDescriptorForType(type); } catch (InterfaceDescriptorCreationException ex) { throw new RuntimeException(ex); } descriptor.setPrimaryTable(table); this.tableToDescriptorDictionary.put(table, descriptor); } generateInstanceVariablesAndMappings(); ensureSpecCompliance(); } private void generateDirectMapping(MWTableDescriptor descriptor, MWColumn column) { Collection mappings = descriptor.allWritableMappingsForField(column); // Don't generate for this field if it is used already (should be in a relationship in this case) // unless it is a primary key (in which case, it will have a relationship and a direct mapping) if (! mappings.isEmpty() && ! column.isPrimaryKey()) return; MWClassAttribute instVar = generateInstanceVariable(descriptor, column); if (column.isPrimaryKey()) { if (!CollectionTools.contains(((MWRelationalTransactionalPolicy) descriptor.getTransactionalPolicy()).getPrimaryKeyPolicy().primaryKeys(), column)) { ((MWRelationalTransactionalPolicy) descriptor.getTransactionalPolicy()).getPrimaryKeyPolicy().addPrimaryKey(column); } } MWDirectToFieldMapping dtfMapping = (MWDirectToFieldMapping) descriptor.addDirectMapping(instVar); dtfMapping.setColumn(column); } private void generateDirectMappings() { for (Iterator stream = this.tableToDescriptorDictionary.keySet().iterator(); stream.hasNext(); ) { MWTable table = (MWTable) stream.next(); MWTableDescriptor descriptor = (MWTableDescriptor) this.tableToDescriptorDictionary.get(table); for (Iterator fields = table.columns(); fields.hasNext(); ) generateDirectMapping(descriptor, (MWColumn) fields.next()); } } private MWClassAttribute generateInstanceVariable(MWTableDescriptor descriptor, String instanceVariableName, MWClass instanceVariableType) { return retrieveOrGenerateInstanceVariable(descriptor, instanceVariableName, instanceVariableType, 0); } private MWClassAttribute retrieveOrGenerateInstanceVariable(MWTableDescriptor descriptor, String instanceVariableName, MWClass instanceVariableType, int instanceVariableArrayDepth) { MWClassAttribute instanceVariable; instanceVariable = retrieveAttribute(descriptor, instanceVariableName, instanceVariableType, instanceVariableArrayDepth); if (instanceVariable == null) { instanceVariable = descriptor.getMWClass().addAttribute(instanceVariableName, instanceVariableType, instanceVariableArrayDepth); } instanceVariable.getModifier().setPrivate(true); return instanceVariable; } private MWClassAttribute retrieveEjb20Attribute(MWTableDescriptor descriptor, String instanceVariableName, MWClass instanceVariableType, int instanceVariableArrayDepth) { //check for existing instance variable that meet criteria first MWClassAttribute instanceVariable = descriptor.getMWClass().attributeNamedFromCombinedAll(instanceVariableName); if (instanceVariable != null) { if (descriptor.mappingForAttribute(instanceVariable) == null) { instanceVariable.setType(instanceVariableType); instanceVariable.setDimensionality(instanceVariableArrayDepth); instanceVariable.getModifier().setPrivate(true); instanceVariable.setEjb20Attribute(true); } } return instanceVariable; } private MWClassAttribute retrieveAttribute(MWTableDescriptor descriptor, String instanceVariableName, MWClass instanceVariableType, int instanceVariableArrayDepth) { //check for existing instance variable that meet criteria first MWClassAttribute instanceVariable = descriptor.getMWClass().attributeNamedFromAll(instanceVariableName); if (instanceVariable != null) { if (descriptor.mappingForAttribute(instanceVariable) == null) { instanceVariable.setType(instanceVariableType); instanceVariable.setDimensionality(instanceVariableArrayDepth); instanceVariable.getModifier().setPrivate(true); } } return instanceVariable; } private MWClassAttribute generateInstanceVariable(MWTableDescriptor descriptor, MWColumn column) { String ivName = column.getName().replace(' ', '_'); ivName = StringTools.convertAllCapsToCamelBack(ivName, false); ivName = NameTools.uniqueJavaNameFor(ivName, descriptor.getMWClass().attributeNames()); MWClass instanceVariableType = this.project.typeNamed(column.javaTypeDeclaration().getJavaClassName()); int instanceVariableArrayDepth = column.javaTypeDeclaration().getArrayDepth(); MWClassAttribute instanceVariable = retrieveOrGenerateInstanceVariable(descriptor, ivName, instanceVariableType, instanceVariableArrayDepth); if (this.generateMethodAccessors) instanceVariable.generateAllAccessors(); return instanceVariable; } private void generateInstanceVariablesAndMappings() { generateRelationshipMappings(); generateDirectMappings(); } private MWOneToManyMapping generateOneToManyMapping(MWTableDescriptor sourceDescriptor, MWClassAttribute attribute) { MWOneToManyMapping oneToManyMapping = sourceDescriptor.addOneToManyMapping(attribute); oneToManyMapping.setUseTransparentIndirection(); return oneToManyMapping; } private MWOneToOneMapping generateOneToOneMapping(MWTableDescriptor sourceDescriptor, MWClassAttribute attribute) { MWOneToOneMapping oneToOneMapping = sourceDescriptor.addOneToOneMapping(attribute); oneToOneMapping.setUseValueHolderIndirection(); return oneToOneMapping; } private void generateReferenceMapping(MWRelationshipHolder relationshipHolder) { MWTableDescriptor sourceDescriptor = getDescriptorForTable(relationshipHolder.getRelationshipSourceTable()); MWTableDescriptor targetDescriptor = getDescriptorForTable(relationshipHolder.getRelationshipTargetTable()); MWClassAttribute newVariable = generateRelationshipInstanceVariable(relationshipHolder, sourceDescriptor, targetDescriptor); MWAbstractTableReferenceMapping referenceMapping; if (relationshipHolder.isOneToOne()) referenceMapping = generateOneToOneMapping(sourceDescriptor, newVariable); else referenceMapping = generateOneToManyMapping(sourceDescriptor, newVariable); this.relationshipToMappingDictionary.put(relationshipHolder, referenceMapping); referenceMapping.setReferenceDescriptor(targetDescriptor); referenceMapping.setReference(relationshipHolder.getReference()); if (relationshipHolder.isForeignKeyInTargetTable() && referenceMapping.isOneToOneMapping()) { for (Iterator i = relationshipHolder.getReference().columnPairs(); i.hasNext(); ) { ((MWOneToOneMapping)referenceMapping).addTargetForeignKey((MWColumnPair) i.next()); } } } private void generateRelationshipMappings() { for (Iterator relationshipsToCreateIterator = this.relationshipsToCreate.iterator(); relationshipsToCreateIterator.hasNext(); ) { MWRelationshipHolder relationshipHolder = (MWRelationshipHolder) relationshipsToCreateIterator.next(); generateReferenceMapping(relationshipHolder); } // must wait until all relationship mappings have been generated if (this.generateBidirectionalRelationships) generateBidirectionalRelationships(); } private String generateUniqueCaseInsensitiveName(String typeName) { int index = 0; boolean notUnique = true; String newTypeName = null; while (notUnique) { newTypeName = typeName + String.valueOf(++index); notUnique = this.getProject().getRepository().typeNamedIgnoreCase(newTypeName) != null; } return newTypeName; } private MWClassAttribute generateRelationshipInstanceVariable(MWRelationshipHolder relationshipHolder, MWTableDescriptor sourceDescriptor, MWTableDescriptor targetDescriptor) { MWClass sourceClass = sourceDescriptor.getMWClass(); String targetClassShortName = targetDescriptor.getMWClass().shortName(); if (targetClassShortName.endsWith("EJB")) targetClassShortName = targetClassShortName.substring(0, targetClassShortName.length() - 3); MWClass targetClass; targetClass = targetDescriptor.getMWClass(); String attributeName = defaultInstanceVariableName(relationshipHolder, sourceClass, targetClassShortName); MWClass attributeType; MWClassAttribute attribute; if (relationshipHolder.isOneToMany()) { // this name will never collide with a reserved word attributeName = NameTools.uniqueNameFor(attributeName + "Collection", sourceClass.attributeNames()); attributeType = getCollectionClass(); attribute = generateInstanceVariable(sourceDescriptor, attributeName, attributeType); attribute.setItemType(targetClass); } else { attributeName = NameTools.uniqueJavaNameFor(attributeName, sourceClass.attributeNames()); attributeType = getValueHolderClass(); attribute = generateInstanceVariable(sourceDescriptor, attributeName, attributeType); attribute.setValueType(targetClass); } if (this.generateMethodAccessors) attribute.generateAllAccessors(); return attribute; } private MWClass getCollectionClass() { return this.project.typeNamed(COLLECTION_TYPE_NAME); } private MWTableDescriptor getDescriptorForTable(MWTable table) { return (MWTableDescriptor) this.tableToDescriptorDictionary.get(table); } public String getPackageName() { return this.packageName; } public MWRelationalProject getProject() { return this.project; } private MWRelationshipHolder getRelationshipToCreate(MWReference reference, boolean foreignKeyInTargetTable) { for (Iterator relsToCreate = this.relationshipsToCreate.iterator(); relsToCreate.hasNext(); ) { MWRelationshipHolder holder = (MWRelationshipHolder) relsToCreate.next(); if (holder.getReference() == reference && holder.isForeignKeyInTargetTable() == foreignKeyInTargetTable) return holder; } return null; } private MWClass getValueHolderClass() { return this.project.typeNamed(VALUE_HOLDER_INTERFACE_TYPE_NAME); } public void setGenerateBidirectionalRelationships(boolean generateBidirectionalRelationships) { this.generateBidirectionalRelationships = generateBidirectionalRelationships; } public void setGenerateEjbs(boolean generateEjbs) { this.generateEjbs = generateEjbs; } public void setGenerateLocalInterfaces(boolean generateLocalInterfaces) { this.generateLocalInterfaces = generateLocalInterfaces; } public void setGenerateMethodAccessors(boolean generateAccessors) { this.generateMethodAccessors = generateAccessors; } public void setGenerateRemoteInterfaces(boolean generateRemoteInterfaces) { this.generateRemoteInterfaces = generateRemoteInterfaces; } public void setPackageName(String packageName) { this.packageName = packageName; } public void setProject(MWRelationalProject project) { this.project = project; } public void setRelationshipsToCreate(Collection relationshipsToCreate) { this.relationshipsToCreate = relationshipsToCreate; } public void setTables(Collection tables) { this.tables = tables; } public boolean shouldGenerateLocalInterfaces() { return this.generateLocalInterfaces; } public boolean shouldGenerateRemoteInterfaces() { return this.generateRemoteInterfaces; } }