/*
* 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.xsd.ui.wizards;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Stack;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDAttributeGroupDefinition;
import org.eclipse.xsd.XSDAttributeUse;
import org.eclipse.xsd.XSDComplexTypeContent;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDConcreteComponent;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDModelGroup;
import org.eclipse.xsd.XSDModelGroupDefinition;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDParticleContent;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.XSDWildcard;
import org.teiid.core.designer.ModelerCoreException;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.types.DatatypeManager;
import org.teiid.designer.core.validation.rules.StringNameValidator;
import org.teiid.designer.metamodels.xsd.XsdUtil;
import org.teiid.designer.ui.viewsupport.RelationalObjectBuilder;
import org.teiid.designer.xsd.ui.ModelerXsdUiConstants;
/**
* Class to externalize the logic for building Virtual Relational from XSD
*
* @since 8.0
*/
public class GenerateVirtualFromXsdHelper {
private static final String SPACER = "_";//$NON-NLS-1$
private static final String NAME = "name";//$NON-NLS-1$
private static final String ANY = "Any";//$NON-NLS-1$
private static final String DEFAULT_SQL = "Select * FROM";//$NON-NLS-1$
private final RelationalObjectBuilder builder;
private final StringNameValidator nameValidator = new StringNameValidator();
private final DatatypeManager dtMgr;
private final MultiStatus status;
private final Resource resource;
private final Collection types;
private final Stack recursionStack = new Stack();
private final Stack elementStack = new Stack();
private String currentRootName = null;
private IProgressMonitor monitor;
public static boolean HEADLESS = false;//Flag to allow for JUnit testing
public GenerateVirtualFromXsdHelper(final MultiStatus status, Resource resource, Collection types) {
CoreArgCheck.isNotNull(status);
CoreArgCheck.isNotNull(resource);
this.status = status;
this.resource = resource;
this.builder = new RelationalObjectBuilder(resource);
this.types = types;
if(HEADLESS) {
this.dtMgr = null;
}else {
this.dtMgr = ModelerCore.getBuiltInTypesManager();
}
}
public void execute(IProgressMonitor monitor) {
boolean requiredStart = ModelerCore.startTxn(false, false, getString("CreateVirtualModelFromSchemaWizard.undoTitle"), this); //$NON-NLS-1$
boolean succeeded = false;
try {
if( resource != null && !types.isEmpty() ) {
int nTables = 0;
String sSize = Integer.toString(types.size());
for(Iterator iter = types.iterator(); iter.hasNext(); ) {
nTables++;
final XSDConcreteComponent next = (XSDConcreteComponent)iter.next();
final String tableName = createTableFromContent(next);
if(!elementStack.isEmpty() ) {
elementStack.clear();
}
if( monitor != null ) {
monitor.worked(1);
monitor.subTask( getString("CreateVirtualModelFromSchemaWizard.incrementalProgress", new Object[] {Integer.toString(nTables), sSize, tableName} )); //$NON-NLS-1$
}
}
}
succeeded = true;
} catch (ModelerCoreException exc) {
addStatus(IStatus.ERROR, exc.getMessage(), exc);
} finally {
//if we started the txn, commit it.
if(requiredStart){
if(!monitor.isCanceled() && succeeded) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
public void doBuild(final IProgressMonitor monitor) {
this.monitor = monitor == null ? new NullProgressMonitor() : monitor;
final String message = getString("CreateVirtualModelFromSchemaWizard.msg"); //$NON-NLS-1$
try {
this.monitor.beginTask(message, types.size()*10);
if (! this.monitor.isCanceled()) {
execute(this.monitor);
}
this.monitor.done();
} catch (Exception e) {
addStatus(IStatus.ERROR, getString("CreateVirtualModelFromSchemaWizard.createError"), e);//$NON-NLS-1$
}
}
//***************************************************************************************************
// PRIVATE METHODS
//***************************************************************************************************
private String getString(final String key, final Object[] args) {
if(HEADLESS) {
//If we are testing, just return the key
return key;
}
return ModelerXsdUiConstants.Util.getString(key, args);
}
private String getString(final String key) {
return getString(key, new Object[] {} );
}
private String createTableFromContent(XSDConcreteComponent content) throws ModelerCoreException{
final String tableDesc = XsdUtil.getDescription(content);
String tableName = ModelerCore.getModelEditor().getName(content);
while( tableName.indexOf('.') > -1) {
tableName = tableName.replace('.', '_');
}
final String tmp = nameValidator.createValidUniqueName(tableName);
if(tmp != null) {
tableName = tmp;
}
final EObject vTable = builder.createBaseTable(tableName, resource, false, tableDesc);
builder.createXPathNIS(vTable, content);
Collection columnsList = new ArrayList();
XSDComplexTypeDefinition ctd = content instanceof XSDComplexTypeDefinition ? (XSDComplexTypeDefinition)content : null;
if(content instanceof XSDElementDeclaration) {
elementStack.push(content);
final XSDTypeDefinition type = ((XSDElementDeclaration)content).getTypeDefinition();
if(type instanceof XSDComplexTypeDefinition) {
ctd = (XSDComplexTypeDefinition)type;
}else {
processContent(type, resource, vTable, columnsList);
}
}
XSDModelGroup group = XsdUtil.getCompositor(ctd);
if(group != null ) {
Iterator particles = group.getParticles().iterator();
while(particles != null && particles.hasNext() ) {
XSDParticle nextPart = (XSDParticle)particles.next();
processContent(nextPart.getContent(), resource, vTable, columnsList);
}
}else if(ctd != null && ctd.getContent() instanceof XSDTypeDefinition) {
processContent(ctd.getContent(), resource, vTable, columnsList);
}else if(ctd != null && ctd.getContent() instanceof XSDAttributeDeclaration) {
processContent(ctd.getContent(), resource, vTable, columnsList);
}else {
final String contentType = (ctd == null || ctd.getContent() == null) ? "null" : ctd.getContent().getClass().getName(); //$NON-NLS-1$
final String msg = getString("CreateVirtualModelFromSchemaWizard.invalidContent", new Object[] {contentType}); //$NON-NLS-1$
addStatus(IStatus.ERROR, msg, null);
}
if( !columnsList.isEmpty() ) {
builder.addColumns(vTable, columnsList);
}
builder.createTransformation(vTable, DEFAULT_SQL);
return tableName;
}
private void processContent(final XSDConcreteComponent content, final Resource resource, final EObject vTable, final Collection columnList) throws ModelerCoreException{
if(content instanceof XSDElementDeclaration) {
final XSDElementDeclaration element = resolveElement( (XSDElementDeclaration)content);
final XSDElementDeclaration parentElement = elementStack.isEmpty() ? null : (XSDElementDeclaration)elementStack.peek();
currentRootName = parentElement == null ? element.getName() : parentElement.getName() + SPACER + element.getName();
elementStack.push(element);
final XSDTypeDefinition typeDefn = element.getType();
if(typeDefn instanceof XSDComplexTypeDefinition) {
addColumnsForComplexType((XSDComplexTypeDefinition)typeDefn, resource, vTable, columnList);
}else {
processContent(typeDefn, resource, vTable, columnList);
}
if(!elementStack.isEmpty() ) {
elementStack.pop();
}
}else if(content instanceof XSDComplexTypeDefinition) {
final XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition)content;
if(complexType.getName() != null) {
currentRootName = complexType.getName();
}
addColumnsForComplexType(complexType, resource, vTable, columnList);
}else if(content instanceof XSDSimpleTypeDefinition) {
XSDSimpleTypeDefinition simpleTypeDefn = (XSDSimpleTypeDefinition)content;
createColumn(columnList, vTable, simpleTypeDefn);
}else if(content instanceof XSDAttributeDeclaration) {
final XSDAttributeDeclaration attr = resolveAttribute( (XSDAttributeDeclaration)content);
XSDSimpleTypeDefinition simpleTypeDefn = attr.getTypeDefinition();
final XSDElementDeclaration parentElement = elementStack.isEmpty() ? null : (XSDElementDeclaration)elementStack.peek();
currentRootName = parentElement == null ? attr.getName() : parentElement.getName() + SPACER + attr.getName();
createColumn(columnList, vTable, simpleTypeDefn);
}else if(content instanceof XSDAttributeUse){
final XSDAttributeDeclaration attr = resolveAttribute( ((XSDAttributeUse)content).getAttributeDeclaration() );
XSDSimpleTypeDefinition simpleTypeDefn = attr.getTypeDefinition();
final XSDElementDeclaration parentElement = elementStack.isEmpty() ? null : (XSDElementDeclaration)elementStack.peek();
currentRootName = parentElement == null ? attr.getName() : parentElement.getName() + SPACER + attr.getName();
createColumn(columnList, vTable, simpleTypeDefn);
}else if(content instanceof XSDAttributeGroupDefinition) {
final XSDAttributeGroupDefinition attGroup = (XSDAttributeGroupDefinition)content;
final Iterator atts = attGroup.getAttributeUses().iterator();
while(atts.hasNext() ) {
processContent( (XSDConcreteComponent)atts.next(), resource, vTable, columnList);
}
}else if(content instanceof XSDWildcard){
final String tableName = getName(vTable);
currentRootName = tableName == null ? ANY : tableName + SPACER + ANY;
final String tmp = nameValidator.createValidUniqueName(currentRootName);
if(tmp != null) {
currentRootName = tmp;
}
createColumn(columnList, vTable, null);
}else {
final String contentType = content == null ? "null" : content.getClass().getName(); //$NON-NLS-1$
final String msg = getString("CreateVirtualModelFromSchemaWizard.unexpectedContent", new Object[] {contentType}); //$NON-NLS-1$
addStatus(IStatus.ERROR, msg, null);
}
}
private String getName(final EObject eObj) {
if(eObj == null) {
return null;
}
final EStructuralFeature name = eObj.eClass().getEStructuralFeature(NAME);
if(name != null) {
return (String)eObj.eGet(name);
}
return null;
}
private void createColumn(final Collection columnList, final EObject vTable, XSDSimpleTypeDefinition simpleTypeDefn) throws ModelerCoreException{
//Use the passed in type to capture property values, use the builtIn type for that type
//to calculate name and to use as the dataType for the column
boolean isAny = simpleTypeDefn == null;
XSDSimpleTypeDefinition builtInType = simpleTypeDefn;
if(HEADLESS) {
//If testing, can't use DTMgr, so just climb to the first type below ANY.
boolean done = false;
while(!done && builtInType != null && builtInType != builtInType.getBaseTypeDefinition() ) {
final XSDSimpleTypeDefinition tmp = builtInType.getBaseTypeDefinition();
if(tmp == null || (tmp.getName() != null && tmp.getName().equals("anySimpleType") ) ) { //$NON-NLS-1$
done = true;
}else {
builtInType = builtInType.getBaseTypeDefinition();
}
}
}else {
while(builtInType != null && !dtMgr.isEnterpriseDatatype(builtInType) && builtInType != builtInType.getBaseTypeDefinition() ) {
builtInType = builtInType.getBaseTypeDefinition();
}
}
if(isAny && !HEADLESS) {
final String msg = getString("CreateVirtualModelFromSchemaWizard.wildcard", new Object[] {currentRootName}); //$NON-NLS-1$
addStatus(IStatus.INFO, msg, null);
builtInType = (XSDSimpleTypeDefinition)dtMgr.getAnySimpleType();
}
if(builtInType == null) {
final String msg = getString("CreateVirtualModelFromSchemaWizard.noDt", new Object[] {currentRootName}); //$NON-NLS-1$
addStatus(IStatus.ERROR, msg, null);
return;
}
if(currentRootName == null) {
currentRootName = builtInType.getName();
}
final String tmp = nameValidator.createValidUniqueName(currentRootName);
if(tmp != null) {
currentRootName = tmp;
}
String columnDesc = XsdUtil.getDescription(simpleTypeDefn);
final EObject nextCol = builder.createColumn(currentRootName, vTable, columnDesc, builtInType, simpleTypeDefn);
if(nextCol != null) {
builder.createColXPathNIS(nextCol, elementStack);
columnList.add(nextCol);
}
}
private void addColumnsForComplexType(XSDComplexTypeDefinition complexTypeDefn, Resource resource, EObject vTable, Collection columnsList) throws ModelerCoreException {
if(recursionStack.contains(complexTypeDefn) ) {
return;
}
recursionStack.push(complexTypeDefn);
XSDModelGroup group = XsdUtil.getCompositor(complexTypeDefn);
final XSDComplexTypeContent content = complexTypeDefn.getContent();
if(group != null) {
processModelGroup(group, resource, vTable, columnsList);
}else if(content instanceof XSDComplexTypeDefinition) {
addColumnsForComplexType( (XSDComplexTypeDefinition)content, resource, vTable, columnsList);
}else if(content instanceof XSDSimpleTypeDefinition) {
processContent(content, resource, vTable, columnsList);
}else if(content instanceof XSDWildcard){
processContent(content, resource, vTable, columnsList);
}else if(content != null) {
final String contentType = content.getClass().getName();
final String msg = getString("CreateVirtualModelFromSchemaWizard.unexpectedContent", new Object[] {contentType}); //$NON-NLS-1$
addStatus(IStatus.ERROR, msg, null);
}
final Iterator atts = complexTypeDefn.getAttributeContents().iterator();
while(atts.hasNext() ) {
final XSDConcreteComponent next = (XSDConcreteComponent)atts.next();
processContent(next, resource, vTable, columnsList);
}
if(!recursionStack.isEmpty() ) {
recursionStack.pop();
}
}
private void processModelGroup(final XSDModelGroup group, final Resource resource, final EObject vTable, final Collection columnsList) throws ModelerCoreException {
if(group == null) {
return;
}
Iterator particles = group.getParticles().iterator();
while( particles.hasNext() ) {
XSDParticle nextPart = (XSDParticle)particles.next();
XSDParticleContent partContent = nextPart.getContent();
if( partContent instanceof XSDElementDeclaration ) {
final XSDElementDeclaration elementContent = resolveElement( (XSDElementDeclaration)partContent );
processContent(elementContent, resource, vTable, columnsList);
}else if(partContent instanceof XSDAttributeDeclaration) {
final XSDAttributeDeclaration attr = (XSDAttributeDeclaration)partContent;
currentRootName = attr.getName();
processContent(attr, resource, vTable, columnsList);
}else if(partContent instanceof XSDModelGroupDefinition) {
XSDModelGroup childGroup = ((XSDModelGroupDefinition)partContent).getModelGroup();
processModelGroup(childGroup, resource, vTable, columnsList);
}else if(partContent instanceof XSDWildcard){
processContent(partContent, resource, vTable, columnsList);
}else {
final String contentType = partContent == null ? "null" : partContent.getClass().getName(); //$NON-NLS-1$
final String msg = getString("CreateVirtualModelFromSchemaWizard.unexpectedContent", new Object[] {contentType}); //$NON-NLS-1$
addStatus(IStatus.ERROR, msg, null);
}
}
}
private XSDElementDeclaration resolveElement(final XSDElementDeclaration element) {
if(element == null || element.getResolvedElementDeclaration() == element) {
return element;
}
return resolveElement(element.getResolvedElementDeclaration() );
}
private XSDAttributeDeclaration resolveAttribute(final XSDAttributeDeclaration attr) {
if(attr == null || attr.getResolvedAttributeDeclaration() == attr) {
return attr;
}
return resolveAttribute(attr.getResolvedAttributeDeclaration() );
}
private void addStatus(final int severity, final String message, final Throwable ex) {
final Status sts = new Status(severity, ModelerXsdUiConstants.PLUGIN_ID, 0, message, ex);
status.add(sts);
}
}