/*
* 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.jdbc.relational.impl;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.designer.jdbc.JdbcException;
import org.teiid.designer.jdbc.JdbcImportSettings;
import org.teiid.designer.jdbc.metadata.JdbcCatalog;
import org.teiid.designer.jdbc.metadata.JdbcDatabase;
import org.teiid.designer.jdbc.metadata.JdbcNode;
import org.teiid.designer.jdbc.metadata.JdbcNodeVisitor;
import org.teiid.designer.jdbc.metadata.JdbcProcedure;
import org.teiid.designer.jdbc.metadata.JdbcSchema;
import org.teiid.designer.jdbc.metadata.JdbcTable;
/**
* The JdbcModelStructure captures the structure of those JdbcNodes that are to be placed into the Relational model. This
* structure is possibly different than the structure of the JdbcDatabase tree because this JdbcModelStructure excludes those
* JdbcNode objects that:
* <ul>
* <li>are {@link JdbcNode#getSelectionMode() unselected}, OR</li>
* <li>do not represent {@link JdbcNode#isDatabaseObject() physical database objects}, OR</li>
* <li>are {@link JdbcCatalog JdbcCatalog} instances when the {@link JdbcImportSettings import settings} specify that
* {@link JdbcImportSettings#isCreateCatalogsInModel() catalogs} are to be placed in the model, OR</li>
* <li>are {@link JdbcSchema JdbcSchema} instances specify that {@link JdbcImportSettings#isCreateSchemasInModel() schemas} are to
* be placed in the model</li> </li>
* </ul>
*
* @since 8.0
*/
public class JdbcModelStructure {
/**
* Build and populate a new instance of the JdbcModelStructure using the supplied {@link JdbcDatabase} tree.
*
* @param dbNode the database structure; may not be null
* @return a new JdbcModelStructure; never null
* @throws JdbcException if there is a problem navigating the JdbcDatabase tree
*/
public static JdbcModelStructure build( final Context context ) throws JdbcException {
final JdbcDatabase dbNode = context.getJdbcDatabase();
final JdbcImportSettings settings = context.getJdbcImportSettings();
final boolean includeCatalogs = settings.isCreateCatalogsInModel();
final boolean includeSchemas = settings.isCreateSchemasInModel();
return build(dbNode, includeCatalogs, includeSchemas);
}
/**
* Build and populate a new instance of the JdbcModelStructure using the supplied {@link JdbcDatabase} tree.
*
* @param dbNode the database structure; may not be null
* @return a new JdbcModelStructure; never null
* @throws JdbcException if there is a problem navigating the JdbcDatabase tree
*/
protected static JdbcModelStructure build( final JdbcDatabase dbNode,
final boolean includeCatalog,
final boolean includeSchema ) throws JdbcException {
final JdbcModelStructure model = new JdbcModelStructure();
// Define the visitor
final JdbcNodeVisitor visitor = new JdbcNodeVisitor() {
@Override
public boolean visit( final JdbcNode node ) {
// if the node is unselected, return and don't visit children
if (node.getSelectionMode() == JdbcNode.UNSELECTED) {
return false;
}
// Don't process catalog or schema nodes if excluded, but always process their children
if (node instanceof JdbcCatalog && !includeCatalog) {
return true;
}
if (node instanceof JdbcSchema && !includeSchema) {
return true;
}
// Get the parent database object for this node ...
final JdbcNode parent = node.getParentDatabaseObject(includeCatalog, includeSchema);
if (node.isDatabaseObject()) {
// Register the node under the parent; parent is null if node is a root-level object
model.addChild(parent, node);
}
model.incrementNodeCount();
if (node instanceof JdbcTable) {
model.incrementTableAndProcedureCount();
} else if (node instanceof JdbcProcedure) {
model.incrementTableAndProcedureCount();
} else if (node instanceof JdbcCatalog) {
// model.incrementTableAndProcedureCount();
} else if (node instanceof JdbcSchema) {
// model.incrementTableAndProcedureCount();
}
return true;
}
};
// Navigate the JdbcDatabase tree using a visitor
dbNode.accept(visitor, JdbcNode.DEPTH_INFINITE);
return model;
}
private final Map childrenForParent;
private final Map parentForChild;
private int totalNodeCount;
private int totalTablesAndProcedureCount;
protected JdbcModelStructure() {
this.childrenForParent = new HashMap();
this.parentForChild = new HashMap();
}
/**
* Get the children for the supplied parent node. Children should be added to parents using the
* {@link #addChild(JdbcNode, JdbcNode) addChild} method, and not by modifying the result of this method.
*
* @param parent the JdbcNode that is the parent; null if the result is to be the JdbcNode objects that are at the top level
* objects in the model structure
* @return the list of JdbcNode objects that are to appear in the model below the <code>parent</code>; null if there are no
* children under the parent.
*/
public List getChildren( final JdbcNode parent ) {
return (List)this.childrenForParent.get(parent);
}
/**
* Add the supplied JdbcNode as a child of the supplied parent JdbcNode.
*
* @param parent the parent; null if <code>child</code> should be a root-level object in the model
* @param child the child; may not be null
* @return true if the child was successfully added under the parent, or false if the child already is a child under a
* different parent
* @see #getParent(
*/
public boolean addChild( final JdbcNode parent,
final JdbcNode child ) {
CoreArgCheck.isNotNull(child);
// Ensure the child is not already registered under another object ...
final Object existingParent = this.parentForChild.get(child);
if (existingParent != null) {
return false;
}
// Get the existing children ...
List children = (List)this.childrenForParent.get(parent);
if (children == null) {
// There were no children for the parent, so create and add the list
children = new LinkedList();
this.childrenForParent.put(parent, children);
}
// Put the child in the list of children ...
children.add(child);
this.parentForChild.put(child, parent);
return true;
}
/**
* Get the parent for the supplied child.
*
* @param child the child node
* @return the existing parent, or null if the child is not currently registered under the supplied parent
*/
public JdbcNode getParent( final JdbcNode child ) {
return (JdbcNode)this.parentForChild.get(child);
}
/**
* Remove the supplied JdbcNode from the children of its current parent.
*
* @param child the child to be removed from its parent; may not be null
* @return the JdbcNode that was the parent of the <code>child</code>, or null if <code>child</code> was not a child of any
* parent.
*/
public JdbcNode removeChild( final JdbcNode child ) {
CoreArgCheck.isNotNull(child);
final JdbcNode parent = (JdbcNode)this.parentForChild.remove(child);
if (parent != null) {
// Get the children for the parent and remove the child from the list
final List children = this.getChildren(parent);
CoreArgCheck.isNotNull(children);
final boolean removed = children.remove(child);
CoreArgCheck.isTrue(removed, "There were no children for child even though child had a parent"); //$NON-NLS-1$
}
return parent;
}
// /**
// * Set the children for the supplied parent node.
// * @param parent the JdbcNode that is the parent; null if the
// * result is to be the JdbcNode objects that are at the top level
// * objects in the model structure
// * @param children the list of JdbcNode objects that are to appear
// * in the model below the <code>parent</code>
// * @return the previous children, if any; null if there were no
// * children previously defined for the supplied parent
// */
// public List setChildren( final JdbcNode parent, final List children ) {
// return (List) this.childrenForParent.put(parent,children);
// }
public void print( final PrintStream stream ) {
printChildren(null, " ", "/", stream); //$NON-NLS-1$ //$NON-NLS-2$
}
public void printChildren( final JdbcNode parent,
final String parentPath,
final String delim,
final PrintStream stream ) {
final List children = this.getChildren(parent);
if (children == null) {
return;
}
final Iterator iter = children.iterator();
while (iter.hasNext()) {
final JdbcNode child = (JdbcNode)iter.next();
final String childPath = parentPath + delim + child.getName();
stream.println(childPath);
printChildren(child, childPath, delim, stream);
}
}
public void print( final StringBuffer sb ) {
printChildren(null, " ", "/", sb); //$NON-NLS-1$ //$NON-NLS-2$
}
public void printChildren( final JdbcNode parent,
final String parentPath,
final String delim,
final StringBuffer sb ) {
final List children = this.getChildren(parent);
if (children == null) {
return;
}
final Iterator iter = children.iterator();
while (iter.hasNext()) {
final JdbcNode child = (JdbcNode)iter.next();
final String childPath = parentPath + delim + child.getName();
sb.append(childPath);
sb.append("\n"); //$NON-NLS-1$
printChildren(child, childPath, delim, sb);
}
}
/**
* @return
*/
public int getTotalNodeCount() {
return totalNodeCount;
}
/**
* @return
*/
public int getTotalTablesAndProceduresCount() {
return totalTablesAndProcedureCount;
}
/**
* @param i
*/
public void setTotalNodeCount( int i ) {
totalNodeCount = i;
}
protected void incrementNodeCount() {
++totalNodeCount;
}
protected void incrementTableAndProcedureCount() {
++totalTablesAndProcedureCount;
}
}