/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.schema.tools.processing.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.teiid.designer.schema.tools.model.schema.Column;
import org.teiid.designer.schema.tools.model.schema.Relationship;
import org.teiid.designer.schema.tools.model.schema.SchemaModel;
import org.teiid.designer.schema.tools.model.schema.SchemaObject;
import org.teiid.designer.schema.tools.processing.RelationshipProcessor;
import org.teiid.designer.schema.tools.processing.RelationshipRules;
/**
* @since 8.0
*/
public abstract class BaseRelationshipProcessor implements RelationshipProcessor {
RelationshipRules rules;
protected Map tableRelationships; // key: Relationship, value Integer - Relationship type
protected SchemaModel schemaModel;
public BaseRelationshipProcessor() {
tableRelationships = new HashMap();
}
protected void setSechemaModel(SchemaModel model) {
this.schemaModel = model;
}
@Override
public void addRelationship(String key, Integer value) {
tableRelationships.put(key, value);
}
@Override
public void setRelationshipRules(RelationshipRules rules) {
this.rules = rules;
}
protected int calculateCValue(List parents) {
int C_value = -3;
for (Iterator parentIter = parents.iterator(); parentIter.hasNext();) {
Object o = parentIter.next();
Relationship tableRelationship = (Relationship) o;
int maxOccursThisLoop = tableRelationship.getMaxOccurs();
if (C_value == -3) {
C_value = maxOccursThisLoop;
} else if (C_value != maxOccursThisLoop) {
C_value = -2;
break;
}
}
return C_value;
}
protected void removeRecursiveMerges(List elements) {
for (Iterator iter = elements.iterator(); iter.hasNext();) {
SchemaObject element = (SchemaObject) iter.next();
LinkedList fullPath = new LinkedList();
LinkedList mergedPath = new LinkedList();
removeRecursiveMergesForTable(element, fullPath, mergedPath);
}
}
protected void removeRecursiveMergesForTable(SchemaObject element, LinkedList fullPath,
LinkedList mergedPath) {
fullPath.addLast(element);
mergedPath.addLast(element);
for (Iterator iter = element.getChildren().iterator(); iter.hasNext();) {
Object o = iter.next();
Relationship tableRelationship = (Relationship) o;
SchemaObject child = tableRelationship.getChild();
String key = child.getSimpleName() + ':' + child.getNamespace();
Integer relation = (Integer)tableRelationships.get(key);
int representation = relation.intValue();
// TODO: check if we have arrived at a recursive merge
LinkedList mergedPathParam = mergedPath;
if (representation == Relationship.MERGE_IN_PARENT_SINGLE
|| representation == Relationship.MERGE_IN_PARENT_MULTIPLE) {
if (mergedPath.contains(child)) {
if (representation == Relationship.MERGE_IN_PARENT_SINGLE) {
representation = Relationship.KEY_IN_PARENT_SINGLE;
} else if (representation == Relationship.MERGE_IN_PARENT_MULTIPLE) {
representation = Relationship.KEY_IN_PARENT_MULTIPLE;
}
SchemaObject parent = tableRelationship.getParent();
parent.setAllParentRepresentations(representation, this);
mergedPathParam = new LinkedList();
}
} else {
mergedPathParam = new LinkedList();
continue;
}
if (fullPath.contains(child)) {
continue;
}
removeRecursiveMergesForTable(child, fullPath, mergedPathParam);
mergedPath.removeLast();
fullPath.removeLast();
}
}
protected void qualifyDuplicateMergedTableNames() {
List processedTables = new ArrayList();
for (Iterator iter = schemaModel.getElements().iterator(); iter.hasNext(); ) {
Object o = iter.next();
SchemaObject table = (SchemaObject)o;
qualifyDuplicateMergedChildTableNames(table, processedTables);
}
}
protected void qualifyDuplicateMergedChildTableNames(SchemaObject table, List processedTables)
{
if (processedTables.contains(table)) {
return;
}
// Add before to prevent infinite recursion
processedTables.add(table);
List children = table.getChildren();
for (Iterator iter = children.iterator(); iter.hasNext(); ) {
Object oTableRelationship = iter.next();
Relationship tableRelationship = (Relationship)oTableRelationship;
qualifyDuplicateMergedChildTableNames(tableRelationship.getChild(), processedTables);
}
checkForDuplicateMergedChildNames(children);
}
protected void checkForDuplicateMergedChildNames(List tableRelationships)
{
Map tablesByName = new HashMap();
for (Iterator allTablesIter = tableRelationships.iterator(); allTablesIter.hasNext();) {
Object o = allTablesIter.next();
Relationship tableRelationship = (Relationship)o;
int representation = tableRelationship.getType();
if (representation != Relationship.MERGE_IN_PARENT_SINGLE &&
representation != Relationship.MERGE_IN_PARENT_MULTIPLE)
{
continue;
}
SchemaObject table = tableRelationship.getChild();
String name = table.getSimpleName();
Object oExisting = tablesByName.get(name);
if (oExisting == null) {
tablesByName.put(name, table);
}
else {
SchemaObject existing = (SchemaObject)oExisting;
existing.setMustBeQualified();
table.setMustBeQualified();
}
}
}
protected void mergeRelationships() {
List processedTables = new ArrayList();
for (Iterator iter = schemaModel.getElements().iterator(); iter.hasNext(); ) {
Object o = iter.next();
SchemaObject element = (SchemaObject)o;
if(schemaModel.isSelectedRootElement(element)) {
mergeChildRelationships(element, processedTables);
}
}
}
/**
* A recursive function that walks down to the end of the graph of relationships of the
* supplied table and the walks back up the graph merging child schemaModel into parents as
* appropriate.
*
* This function is called for each table in the list of known schemaModel, but appends to the
* processedTables List so that it operates only once on each table, and does not infinitly
* recurse on circular relationships.
*
* @param table The table at the top of the graph.
* @param processedTables the list of schemaModel that have been processed by this function.
*/
protected void mergeChildRelationships(SchemaObject table, List processedTables)
{
if (processedTables.contains(table)) {
return;
}
// Add before to prevent infinite recursion
processedTables.add(table);
table.setWithinSelectedHierarchy(true);
// This is a slightly precarious recursive algorithm, in that children are allowed
// to add elements to the end of their parents' list of children. That's why
// we use indexed iteration rather that iterators.
Object[] children = table.getChildren().toArray();
for (int i = 0; i < children.length; i++) {
Object relObject = children[i];
Relationship tableRelationship = (Relationship)relObject;
// depth first: merge the child's children first because they themselves
// may need to get merged into the table
SchemaObject child = tableRelationship.getChild();
mergeChildRelationships(child, processedTables);
String key = child.getSimpleName() + ':' + child.getNamespace();
if(null != tableRelationships.get(key)) {
int representation = ((Integer)tableRelationships.get(key)).intValue();
if (representation == Relationship.MERGE_IN_PARENT_SINGLE ||
representation == Relationship.MERGE_IN_PARENT_MULTIPLE) {
mergeChild(table, tableRelationship);
// The following statement has the effect of removing the relationship
// from the list, so we need to mess with the loop variable.
tableRelationship.removeRelationship();
}
}
}
}
/**
* Merges the columns and TableRelationships of a child into its parent(s).
* @param parent The table to merge into.
* @param tableRelationship The Relationship to the child being merged.
*/
protected void mergeChild(SchemaObject parent, Relationship tableRelationship)
{
SchemaObject child = tableRelationship.getChild();
child.setWithinSelectedHierarchy(false);
Object[] cols = child.getAttributes().toArray();
for (int i = 0; i < cols.length; i++ ) {
Column col = (Column)cols[i];
int maxOccurs = tableRelationship.getMaxOccurs();
for (int iOccurrence = 1; iOccurrence <= maxOccurs; iOccurrence++) {
int iOccurenceParam = maxOccurs > 1 ? iOccurrence : -1;
col.mergeIntoParent(tableRelationship, iOccurenceParam);
}
}
pullUpGrandChildRelationships(child.getParents(), child.getChildren());
}
/**
* Merges each child Relationship and each parent Relationship of a
* table being merged and then deletes the Relationship between the merged table
* and its children.
* @param parentRelationships the List of parent TableRelationships to a table
* @param grandChildren the List of child TableRelationships to a table
*/
protected void pullUpGrandChildRelationships(List parentRelationships, List grandChildren) {
List newRelationships = new ArrayList();
List foldedRelationships = new ArrayList();
//Create the merged relationship and recorded both the new relationship and the folded one.
for(Iterator iter = grandChildren.iterator(); iter.hasNext();) {
Object o = iter.next();
Relationship grandChild = (Relationship)o;
for(Iterator pIter = parentRelationships.iterator(); pIter.hasNext(); ) {
Relationship tableRelationship = (Relationship)pIter.next();
Relationship mergedRelationship = tableRelationship.merge(grandChild);
newRelationships.add(mergedRelationship);
foldedRelationships.add(grandChild);
}
}
// Remove all of the folded ones.
for (Iterator iter = foldedRelationships.iterator(); iter.hasNext(); ) {
Object o = iter.next();
Relationship foldedRelationship = (Relationship)o;
foldedRelationship.removeRelationship();
}
// Add the new ones
for (Iterator iter = newRelationships.iterator(); iter.hasNext(); ) {
Object o = iter.next();
Relationship newRelationship = (Relationship)o;
newRelationship.addNewRelationship();
}
}
protected void removeFullyMergedTables()
{
List nonMergedTables = new ArrayList();
for (Iterator iter = schemaModel.getElements().iterator(); iter.hasNext(); ) {
Object o = iter.next();
SchemaObject table = (SchemaObject)o;
if (table.isWithinSelectedHierarchy()) {
nonMergedTables.add(table);
continue;
}
}
schemaModel.setElements(nonMergedTables);
}
protected void qualifyDuplicateNonMergedTableNames() {
Map tablesByName = new HashMap();
for (Iterator allTablesIter = schemaModel.getElements().iterator(); allTablesIter.hasNext();) {
Object o = allTablesIter.next();
SchemaObject table = (SchemaObject)o;
String name = table.getSimpleName();
Object oExisting = tablesByName.get(name);
if (oExisting == null) {
tablesByName.put(name, table);
}
else {
SchemaObject existing = (SchemaObject)oExisting;
existing.setMustBeQualified();
table.setMustBeQualified();
}
}
}
}