/*
* 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.model.schema.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.xsd.XSDComplexTypeContent;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
import org.teiid.designer.schema.tools.ToolsPlugin;
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.SchemaObject;
import org.teiid.designer.schema.tools.processing.RelationshipProcessor;
/**
* @since 8.0
*/
public abstract class BaseSchemaObject implements SchemaObject {
protected boolean doesNotHaveUniqueName;
protected String fileName = new String();
protected boolean availableRoot = false;
boolean recursivityDetector = false;
/**
* Indicates if this table is, or falls under, a selected root element.
*/
protected boolean withinSelectedHierarchy = false;
private String prefix;
protected List parents = new ArrayList();
protected List attributes = new ArrayList();
protected List children = new ArrayList();
// Indicates if the SchemaObject should be represented as a Table in the final model (the xmi that is)
protected boolean representAsTable = false;
// User Selections
boolean useAsRoot = false;
protected XSDTypeDefinition type;
protected XSDSchema schema;
protected BaseSchemaObject( String namespacePrefix,
XSDTypeDefinition type,
XSDSchema schema ) {
this.prefix = namespacePrefix;
this.type = type;
this.schema = schema;
}
@Override
public void setMustBeQualified() {
doesNotHaveUniqueName = true;
}
@Override
public void setFileName( String fileName ) {
this.fileName = fileName;
}
@Override
public String getFileName() {
return fileName;
}
@Override
public boolean isWithinSelectedHierarchy() {
return withinSelectedHierarchy;
}
@Override
public void setWithinSelectedHierarchy( boolean under ) {
withinSelectedHierarchy = under;
}
@Override
public boolean isCanBeRoot() {
if (!availableRoot || children.size() == 0 || parents.size() > 0) {
return false;
}
return true;
}
protected ArrayList copyRelationshipArray( List relationships,
SchemaModelCopyTraversalContext ctx ) {
ArrayList relationshipsCopy = new ArrayList(relationships.size());
Iterator iter = relationships.iterator();
while (iter.hasNext()) {
SimpleRelationship rel = (SimpleRelationship)iter.next();
relationshipsCopy.add(rel.copy(ctx));
}
return relationshipsCopy;
}
protected ArrayList copyAttributesArray( List list,
SchemaObject copiedElement ) {
ArrayList attributesCopy = new ArrayList(list.size());
Iterator iter = list.iterator();
while (iter.hasNext()) {
Column column = (Column)iter.next();
Column copy = column.copy();
copy.setTable(copiedElement);
attributesCopy.add(copy);
}
return attributesCopy;
}
@Override
public void addParent( SchemaObject parent,
int minOccurs,
int maxOccurs ) {
if (parent == null) {
availableRoot = true;
return;
}
boolean alreadyThere = false;
// We can cast to SimpleRelationship here, because during the initial
// analysis
// only SimpleTableRelationships are created. MergedTableRelationships
// are not created
// until after user options have been set.
for (Iterator iter = parents.iterator(); iter.hasNext();) {
Object o = iter.next();
SimpleRelationship existingTableRelationship = (SimpleRelationship)o;
SchemaObject existingTable = existingTableRelationship.getParent();
if (existingTable == parent) {
// this child appears more than once in the content model for
// the parent
// Instead of adding it again, we'll just make sure that the
// cardinality is correct
alreadyThere = true;
existingTableRelationship.setMinOccurs(existingTableRelationship.getMinOccurs() + minOccurs);
if ((existingTableRelationship.getMaxOccurs() != Relationship.UNBOUNDED) && (maxOccurs != Relationship.UNBOUNDED)) {
existingTableRelationship.setMaxOccurs(existingTableRelationship.getMaxOccurs() + maxOccurs);
} else {
existingTableRelationship.setMaxOccurs(Relationship.UNBOUNDED);
}
}
}
if (!alreadyThere) {
Relationship relationship = new SimpleRelationship(parent, this, minOccurs, maxOccurs);
relationship.addNewRelationship();
}
}
@Override
public void addAttribute( Column col ) {
col.setTable(this);
attributes.add(col);
}
@Override
public String recursiveGetXpath() {
if (recursivityDetector) {
return null;
}
recursivityDetector = true;
try {
HashSet paths = new HashSet();
ArrayList xpaths = new ArrayList();
for (Iterator iter = parents.iterator(); iter.hasNext();) {
Object o = iter.next();
Relationship tableRelationship = (Relationship)o;
SchemaObject element = tableRelationship.getParent();
String parentXpath = element.recursiveGetXpath();
if (parentXpath == null) {
// recursive references. Abort! (all the way up)
return null;
}
String childRelXpath = tableRelationship.getChildRelativeXpath();
String leafXpath = getRelativeXpath();
if (!(childRelXpath.endsWith(leafXpath))) {
// Logic error
throw new IllegalStateException(ToolsPlugin.Util.getString("BaseSchemaObject.illegalState")); //$NON-NLS-1$
}
String xpathPlusLeaf;
if (parentXpath.endsWith("/")) { //$NON-NLS-1$
xpathPlusLeaf = parentXpath + childRelXpath;
} else {
xpathPlusLeaf = parentXpath + "/" + childRelXpath; //$NON-NLS-1$
}
String xpathWithoutLeaf = xpathPlusLeaf.substring(0, xpathPlusLeaf.length() - leafXpath.length() - 1);
if (!paths.contains(xpathWithoutLeaf)) {
paths.add(xpathWithoutLeaf);
xpaths.add(xpathWithoutLeaf);
}
}
// There are five possible configurations of parents. Here is how
// they are formatted:
// 1) No parents, just root: /elementname
// 2) Single parent, plus root: (parent/elementname) | /elementname
// 3) Single parent, no root: parent/elementname
// 4) Multiple parents, plus root: (((parent1) |
// (parentn))/elementname) | /elementname
// 5) Multiple parents, no root: ((parent1) | (parentn))/elementname
// Note
// 1) Xpaths need parentheses around them if they contain an | at
// the top level,
// so that they can be included in bigger Xpaths.
// 2) Xpaths do not have a trailing slash. if they are used as a
// parent the slash will be added then.
String qname = getRelativeXpath();
StringBuffer xpath = new StringBuffer();
boolean alsoRoot = this.isUseAsRoot();
if (xpaths.size() == 0) {
// configuration 1
xpath.append("/"); //$NON-NLS-1$
xpath.append(qname);
} else if (xpaths.size() == 1 && alsoRoot) {
// configuration 2
xpath.append("(("); //$NON-NLS-1$
xpath.append(xpaths.get(0));
xpath.append('/');
xpath.append(qname);
xpath.append(") | (/"); //$NON-NLS-1$
xpath.append(qname);
xpath.append("))"); //$NON-NLS-1$
} else if (xpaths.size() == 1 && !alsoRoot) {
// configuration 3
xpath.append(xpaths.get(0));
xpath.append('/');
xpath.append(qname);
} else if (alsoRoot) {
// configuration 4
xpath.append("((("); //$NON-NLS-1$
for (int i = 0; i < xpaths.size(); i++) {
Object o = xpaths.get(i);
String parentXpath = (String)o;
if (i > 0) {
xpath.append(" | "); //$NON-NLS-1$
}
xpath.append("("); //$NON-NLS-1$
xpath.append(parentXpath);
xpath.append(")"); //$NON-NLS-1$
}
xpath.append(")/"); //$NON-NLS-1$
xpath.append(qname);
xpath.append(") | (/"); //$NON-NLS-1$
xpath.append(qname);
xpath.append("))"); //$NON-NLS-1$
} else {
// configuration 5
xpath.append("("); //$NON-NLS-1$
for (int i = 0; i < xpaths.size(); i++) {
Object o = xpaths.get(i);
String parentXpath = (String)o;
if (i > 0) {
xpath.append(" | "); //$NON-NLS-1$
}
xpath.append("("); //$NON-NLS-1$
xpath.append(parentXpath);
xpath.append(")"); //$NON-NLS-1$
}
xpath.append(")/"); //$NON-NLS-1$
xpath.append(qname);
}
return xpath.toString();
} finally {
recursivityDetector = false;
}
}
@Override
public void cascadeRootSelection( boolean b ) {
Set visitedElements = new HashSet();
this.setUseAsRoot(b);
this.setWithinSelectedHierarchy(b);
visitedElements.add(this.getKey());
cascadeRootSelectionImpl(this.getChildren(), b, visitedElements);
}
private void cascadeRootSelectionImpl( List children,
boolean b,
Set visitedElements ) {
for (Iterator iter = children.iterator(); iter.hasNext();) {
Relationship childRelationship = (Relationship)iter.next();
SchemaObject child = childRelationship.getChild();
if (!visitedElements.contains(child.getKey())) {
visitedElements.add(child.getKey());
child.setWithinSelectedHierarchy(b);
cascadeRootSelectionImpl(child.getChildren(), b, visitedElements);
}
}
}
@Override
public String getInputXPath() {
return GetXPath();
}
@Override
public String getOutputXPath() {
return GetXPath();
}
private String GetXPath() {
String xpath = recursiveGetXpath();
if (xpath == null) {
// recursiveness detected
String name = getName();
if (prefix != null && !prefix.equals("")) { //$NON-NLS-1$
xpath = "//" + prefix + ':' + name; //$NON-NLS-1$
} else {
xpath = "//" + name; //$NON-NLS-1$
}
}
return xpath;
}
@Override
public String getRelativeXpath() {
String qname;
String name = getName();
if (prefix != null && !prefix.equals("")) { //$NON-NLS-1$
qname = prefix + ':' + name;
} else {
qname = name;
}
return qname;
}
@Override
public List getParents() {
return parents;
}
@Override
public List getChildren() {
return children;
}
@Override
public List getAttributes() {
return attributes;
}
void mergeChild( Relationship tableRelationship ) {
SchemaObject child = tableRelationship.getChild();
for (Iterator colIter = child.getAttributes().iterator(); colIter.hasNext();) {
Object oCol = colIter.next();
Column col = (Column)oCol;
int maxOccurs = tableRelationship.getMaxOccurs();
for (int iOccurrence = 1; iOccurrence <= maxOccurs; iOccurrence++) {
int iOccurenceParam = maxOccurs > 1 ? iOccurrence : -1;
Column mergedColumn = new MergedColumn(col, tableRelationship, iOccurenceParam);
addAttribute(mergedColumn);
}
}
}
@Override
public void setAllParentRepresentations( int representation,
RelationshipProcessor processor ) {
// TODO: Setting the relationship type into the Relationship was a failed implementation, all vestiges should be removed.
List parents = getParents();
for (Iterator iter = parents.iterator(); iter.hasNext();) {
Object o = iter.next();
Relationship tableRelationship = (Relationship)o;
tableRelationship.setType(representation);
SchemaObject child = tableRelationship.getChild();
String key = child.getSimpleName() + ':' + child.getNamespace();
processor.addRelationship(key, new Integer(representation));
}
}
@Override
public boolean isUseAsRoot() {
return useAsRoot;
}
@Override
public void setUseAsRoot( boolean useAsRoot ) {
this.useAsRoot = useAsRoot;
}
@Override
public boolean isSimpleElement( RelationshipProcessor processor ) {
boolean isSimpleRequest = true;
/*
* for (Iterator iter = children.iterator(); iter.hasNext(); ){
* Relationship child = (Relationship)iter.next(); int representation =
* child.getRepresentation(processor); if(representation !=
* Relationship.MERGE_IN_PARENT_SINGLE || representation !=
* Relationship.MERGE_IN_PARENT_MULTIPLE) { isSimpleRequest = false;
* break; } }
*/return isSimpleRequest;
}
@Override
public boolean representAsTable() {
return representAsTable;
}
@Override
public void setRepresentAsTable( boolean table ) {
this.representAsTable = table;
}
@Override
public List getAllModelColumns() {
ArrayList columns = new ArrayList();
for (Iterator iter = attributes.iterator(); iter.hasNext();) {
Column column = (Column)iter.next();
columns.add(column.getColumnImplementation());
}
return columns;
}
@Override
public List getAttributeList() {
List result;
if (type instanceof XSDComplexTypeDefinition) {
result = ((XSDComplexTypeDefinition)type).getAttributeUses();
} else {
result = new ArrayList();
}
return result;
}
public String getNamespacePrefix() {
return prefix;
}
protected void copy( BaseSchemaObject copy,
SchemaModelCopyTraversalContext ctx ) {
ArrayList attributesCopy = copyAttributesArray(attributes, copy);
copy.attributes = attributesCopy;
ArrayList parentsCopy = copyRelationshipArray(parents, ctx);
copy.parents = parentsCopy;
ArrayList childrenCopy = copyRelationshipArray(children, ctx);
copy.children = childrenCopy;
copy.availableRoot = availableRoot;
copy.doesNotHaveUniqueName = doesNotHaveUniqueName;
copy.fileName = fileName;
copy.useAsRoot = useAsRoot;
copy.withinSelectedHierarchy = withinSelectedHierarchy;
}
@Override
public XSDSchema getSchema() {
return schema;
}
@Override
public boolean hasComplexTypeDefinition() {
return type instanceof XSDComplexTypeDefinition;
}
@Override
public boolean hasSimpleTypeDefinition() {
return type instanceof XSDSimpleTypeDefinition;
}
@Override
public XSDComplexTypeContent getContent() {
XSDComplexTypeContent result = null;
if (type instanceof XSDComplexTypeDefinition) {
XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition)type;
result = complexType.getContentType();
}
return result;
}
@Override
public XSDSimpleTypeDefinition getTextType() {
XSDSimpleTypeDefinition textType = null;
if (hasSimpleTypeDefinition()) {
textType = (XSDSimpleTypeDefinition)getType();
} else if (hasComplexTypeDefinition()) {
XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition)getType();
XSDComplexTypeContent content = complexType.getContentType();
if (content instanceof XSDSimpleTypeDefinition) {
textType = (XSDSimpleTypeDefinition)content;
} else if (complexType.isMixed()) {
textType = getStringType(getSchema());
}
}
return textType;
}
private static XSDSimpleTypeDefinition getStringType( XSDSchema schema ) {
if (stringType == null) {
XSDSchema schemaForSchema = schema.getSchemaForSchema();
Map typeIdMap = schemaForSchema.getSimpleTypeIdMap();
Object o = typeIdMap.get("string"); //$NON-NLS-1$
stringType = (XSDSimpleTypeDefinition)o;
}
return stringType;
}
private static XSDSimpleTypeDefinition stringType = null;
}