package org.teiid.designer.modelgenerator.xsd.procedures; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.xml.namespace.QName; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xsd.XSDTypeDefinition; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.query.QueryValidator; import org.teiid.designer.core.types.DatatypeConstants; import org.teiid.designer.core.workspace.ModelWorkspaceException; import org.teiid.designer.metamodels.relational.Column; import org.teiid.designer.metamodels.relational.DirectionKind; import org.teiid.designer.metamodels.relational.NullableType; import org.teiid.designer.metamodels.relational.Procedure; import org.teiid.designer.metamodels.relational.ProcedureParameter; import org.teiid.designer.metamodels.relational.ProcedureResult; import org.teiid.designer.metamodels.transformation.SqlTransformationMappingRoot; import org.teiid.designer.schema.tools.NameUtil; import org.teiid.designer.transformation.util.TransformationHelper; import org.teiid.designer.transformation.util.TransformationMappingHelper; /** * * Passed along with the parsing of a XSD. * Responsible for determining when we are at the limit of what * can be represented in a procedure result. * * Responsible for keeping a list of procedures created from XSD * so that we do not duplicate. * * @since 8.0 */ public class ResultBuilderTraversalContext extends BaseTraversalContext implements TraversalContext { private ProcedureResult result; private Procedure procedure; //private SqlTransformationMappingRoot transformation; private List<Column> cachedColumns = new ArrayList(); public static final String RESPONSE = "response_"; //$NON-NLS-1$ public static final String XML_IN = "xml_in"; //$NON-NLS-1$ public ResultBuilderTraversalContext(String procedureName, QName namespace, TraversalContext ctx, ProcedureBuilder builder) { super(procedureName, namespace, ctx, builder); } public ResultBuilderTraversalContext(String procedureName, QName namespace, ProcedureBuilder builder) { super(procedureName, namespace, builder); } /* (non-Javadoc) * @See org.teiid.designer.modelgenerator.wsdl.TraversalContext#addResultColumn(java.lang.String, org.eclipse.xsd.XSDTypeDefinition) */ @Override public void addColumn(String name, XSDTypeDefinition type) throws ModelerCoreException { // If the procedure doesn't exist yet. // Don't create a function if there's no data. // for nested elements with no scalars. if(result == null) { procedure = createProcedure(procedureName); result = procedure.getResult(); } // Add a colum to the result for the data. Column resultCol = factory.createColumn(); result.getColumns().add(resultCol); String uniqueName = getUniqueName(resultCol, NameUtil.normalizeName(name)); resultCol.setName(uniqueName); resultCol.setNameInSource(uniqueName); resultCol.setType(datatypeManager.getDatatypeForXsdType(type)); cachedColumns .add(resultCol); //tell the builder to use a new ctx when going futher //down the XML tree. setReachedResultNode(true); } Procedure createProcedure(String procedureNameBase) throws ModelWorkspaceException, ModelerCoreException { procedure = factory.createProcedure(); builder.getSchema().getProcedures().add(procedure); String uniqueName = getUniqueName(procedure, RESPONSE + NameUtil.normalizeName(procedureNameBase)); procedure.setName(uniqueName); procedure.setNameInSource(procedureNameBase); ProcedureParameter param = factory.createProcedureParameter(); procedure.getParameters().add(param); param.setDirection(DirectionKind.IN_LITERAL); param.setName(XML_IN); param.setNameInSource(XML_IN); param.setNullable(NullableType.NO_NULLS_LITERAL); param.setType(datatypeManager .getBuiltInDatatype(DatatypeConstants.BuiltInNames.XML_LITERAL)); ProcedureResult result = factory.createProcedureResult(); procedure.setResult(result); result.setName(NameUtil.normalizeName(procedureNameBase) + IBuilderConstants.V_FUNC_RESULT); result.setNameInSource(procedureNameBase); builder.addProcedure(procedureNameBase); //transformation = builder.getModelResource().getModelTransformations().createNewSqlTransformation(procedure); return procedure; } @Override public void createTransformation() { if(null != procedure) { StringBuffer sqlString = new StringBuffer(); sqlString.append(IBuilderConstants.V_FUNC_PREAMBLE); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_SELECT_FROM_T); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_XMLTABLE); sqlString.append(IBuilderConstants.V_FUNC_OPEN); sqlString.append(IBuilderConstants.V_FUNC_SPACE); if(null != getNamespace() || !getNamespace().getNamespaceURI().isEmpty()) { sqlString.append(IBuilderConstants.V_FUNC_XMLNAMESPACES); sqlString.append(IBuilderConstants.V_FUNC_OPEN); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_DEFAULT); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_QUOTE); sqlString.append(getNamespace().getNamespaceURI()); sqlString.append(IBuilderConstants.V_FUNC_QUOTE); sqlString.append(IBuilderConstants.V_FUNC_CLOSE); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_COMMA); } sqlString.append(IBuilderConstants.V_FUNC_QUOTE); sqlString.append(getPath()); sqlString.append(IBuilderConstants.V_FUNC_QUOTE); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_PASSING); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(XML_IN); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_COLUMNS); boolean firstTime = true; for (Column column : cachedColumns) { if(!firstTime) { sqlString.append(IBuilderConstants.V_FUNC_COMMA); sqlString.append(IBuilderConstants.V_FUNC_SPACE); } sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_DOUBLE_QUOTE); sqlString.append(column.getNameInSource()); sqlString.append(IBuilderConstants.V_FUNC_DOUBLE_QUOTE); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(datatypeManager.getRuntimeTypeName(column.getType())); // Not sure we need the path //sqlString.append(V_FUNC_SPACE); //sqlString.append(column.getName()); firstTime = false; } sqlString.append(IBuilderConstants.V_FUNC_CLOSE); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_AS_T); sqlString.append(IBuilderConstants.V_FUNC_SPACE); sqlString.append(IBuilderConstants.V_FUNC_POSTSCRIPT); SqlTransformationMappingRoot root = (SqlTransformationMappingRoot)TransformationHelper.getTransformationMappingRoot(procedure); TransformationHelper.setSqlString(root, sqlString.toString(), QueryValidator.SELECT_TRNS, true, this); TransformationMappingHelper.reconcileMappingsOnSqlChange(root, this); } } private String getUniqueName( final EObject eObject, final String proposedName ) { CoreArgCheck.isNotNull(eObject); final EStructuralFeature nameFeature = ModelerCore.getModelEditor().getNameFeature(eObject); if (nameFeature != null) { return generateUniqueInternalName(eObject.eContainer() == null ? eObject.eResource().getContents() : eObject.eContainer().eContents(), eObject, nameFeature, proposedName); } return proposedName; } private String generateUniqueInternalName( final EList siblings, final EObject eObject, final EStructuralFeature nameFeature, final String name ) { String newName = name; if (siblings != null) { final Set siblingNames = new HashSet(); for (Iterator it = siblings.iterator(); it.hasNext();) { final EObject child = (EObject)it.next(); if (eObject.getClass().equals(child.getClass())) { siblingNames.add(child.eGet(nameFeature)); } } boolean foundUniqueName = false; int index = 1; while (!foundUniqueName) { if (siblingNames.contains(newName)) { newName = name + String.valueOf(index++); } else { foundUniqueName = true; } } } return newName; } }