/* * 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.xml.factory; import org.eclipse.emf.ecore.EObject; import org.eclipse.xsd.XSDAttributeUse; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDComponent; import org.eclipse.xsd.XSDContentTypeCategory; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDNamedComponent; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.XSDTypeDefinition; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.I18nUtil; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.util.ModelContents; import org.teiid.designer.mapping.factory.IMappableTree; import org.teiid.designer.mapping.factory.ITreeToRelationalMapper; import org.teiid.designer.metamodels.xml.XmlAttribute; import org.teiid.designer.metamodels.xml.XmlChoice; import org.teiid.designer.metamodels.xml.XmlContainerNode; import org.teiid.designer.metamodels.xml.XmlDocumentEntity; import org.teiid.designer.metamodels.xml.XmlDocumentNode; import org.teiid.designer.metamodels.xml.XmlElement; import org.teiid.designer.metamodels.xml.XmlFragment; import org.teiid.designer.metamodels.xsd.XsdUtil; import org.teiid.designer.xml.PluginConstants; /** * XmlFragmentMapper * * @since 8.0 */ public class XmlFragmentMapper implements ITreeToRelationalMapper, PluginConstants { /*----- DESIGN NOTES ---------------------------------------------------------------------------- * - one TransformationMappingRoot for each MappingClass * - TranformationMappingRoot target=XmlFragment * - TransformationMappingRoot input=MappingClass * - TransformationMappingRoot output=XmlElement * - TransformationMappingRoot contains nested Mappings. If an attribute is mapped, it will have * a mapping. If it is not mapped a Mapping could still exist if either input or output is null. *----------------------------------------------------------------------------------------------*/ /////////////////////////////////////////////////////////////////////////////////////////////// // CONSTANTS /////////////////////////////////////////////////////////////////////////////////////////////// private static final String PREFIX = I18nUtil.getPropertyPrefix(XmlFragmentMapper.class); /////////////////////////////////////////////////////////////////////////////////////////////// // FIELDS /////////////////////////////////////////////////////////////////////////////////////////////// private XmlFragment fragment; // folder in old modeler private XmlMappableTree tree; // the IMappableTree for xml // private List mappingRoots; // TransformationMappingRoot collection /////////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS /////////////////////////////////////////////////////////////////////////////////////////////// public XmlFragmentMapper() { } /////////////////////////////////////////////////////////////////////////////////////////////// // METHODS /////////////////////////////////////////////////////////////////////////////////////////////// /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#allowsMappingClass(org.eclipse.emf.ecore.EObject) */ @Override public boolean allowsMappingClass(EObject theTreeNode) { return ((theTreeNode instanceof XmlElement) || (theTreeNode instanceof XmlContainerNode)); } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#allowsStagingTable(org.eclipse.emf.ecore.EObject) */ @Override public boolean allowsStagingTable(EObject theTreeNode) { // can put a StagingTable anywhere that a MappingClass is allowed. return allowsMappingClass(theTreeNode); } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#canIterate(org.eclipse.emf.ecore.EObject) */ @Override public boolean canIterate(EObject theTreeNode) { checkNodeIsXmlDocumentEntity(theTreeNode); int maxOccurs = 0; if (theTreeNode instanceof XmlDocumentNode) { maxOccurs = XsdUtil.getMaxOccursLiteral(((XmlDocumentNode)theTreeNode).getXsdComponent()); } else if (theTreeNode instanceof XmlContainerNode) { maxOccurs = XsdUtil.getMaxOccursLiteral(((XmlContainerNode)theTreeNode).getXsdComponent()); } return ((maxOccurs > 1) || (maxOccurs == -1)); // -1 means unbounded } /** * @throws IllegalArgumentException if the tree node is <code>null</code> * @throws IllegalArgumentException if the tree node is not an <code>XmlDocumentEntity</code> */ private void checkNodeIsXmlDocumentEntity(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); if (!(theTreeNode instanceof XmlDocumentEntity)) { throw new IllegalArgumentException(Util.getString(PREFIX + "invalidType", //$NON-NLS-1$ new Object[] {"XmlDocumentEntity", //$NON-NLS-1$ theTreeNode.getClass().getName()})); } } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#getMappableTree() */ @Override public IMappableTree getMappableTree() { if ( tree == null ) { tree = new XmlMappableTree(this.fragment); } return tree; } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#isContainerNode() */ @Override public boolean isContainerNode(EObject theTreeNode) { //swjTODO: check this logic against the metamodel return theTreeNode instanceof XmlContainerNode; } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#isMappable(org.eclipse.emf.ecore.EObject) */ @Override public boolean isMappable(final EObject theTreeNode) { checkNodeIsXmlDocumentEntity(theTreeNode); if ( theTreeNode instanceof XmlAttribute ) { return isMappable((XmlAttribute)theTreeNode); } if ( theTreeNode instanceof XmlElement ) { return isMappable((XmlElement)theTreeNode); } return false; // all other XML Document objects are not mappable } /** * Determine whether the supplied XmlAttribute is considered mappable. * @param attribute * @return */ public boolean isMappable( final XmlAttribute attribute ) { CoreArgCheck.isNotNull(attribute); if ( attribute.isValueFixed() ) { // The attribute has a fixed value, so it should not be mappable return false; } return true; } /** * Determine whether the supplied XmlElement is considered mappable. * @param attribute * @return */ public boolean isMappable( final XmlElement element ) { CoreArgCheck.isNotNull(element); // Obtain the schema component for the element ... final XSDComponent schemaComponent = element.getXsdComponent(); if ( schemaComponent != null ) { return isElementMappable(schemaComponent); } // else there is no schema component, and ad hoc elements are mappable return true; } /** * Determine whether an XmlElement with the supplied XSDComponent is considered mappable. * @param attribute * @return */ public boolean isElementMappable( final XSDComponent xsdComponent ) { CoreArgCheck.isNotNull(xsdComponent); if (xsdComponent instanceof XSDElementDeclaration) { final XSDElementDeclaration elementDecl = (XSDElementDeclaration)xsdComponent; // Obtain the type of the element declaration (which can be a ref, etc.) ... final XSDTypeDefinition type = XsdUtil.getType(elementDecl); if (type instanceof XSDSimpleTypeDefinition) { // Element with a simple type, therefore mappable ... return true; } else if (type instanceof XSDComplexTypeDefinition) { final XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition)type; // A Complex type that has mixed content is mappable ... if ( complexType.isMixed() ) { return true; } final XSDContentTypeCategory category = complexType.getContentTypeCategory(); final int categoryValue = category.getValue(); switch(categoryValue) { case XSDContentTypeCategory.MIXED: return true; case XSDContentTypeCategory.ELEMENT_ONLY: return false; case XSDContentTypeCategory.EMPTY: return false; case XSDContentTypeCategory.SIMPLE: return true; } return false; } } else if (xsdComponent instanceof XSDComplexTypeDefinition) { // this is a fragment, so it should be mappable ... return true; } return false; } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#isMappingRequired(org.eclipse.emf.ecore.EObject) */ @Override public boolean isMappingRequired(EObject theTreeNode) { checkNodeIsXmlDocumentEntity(theTreeNode); throw new RuntimeException("XmlFragmentMapper.isMappingRequired not yet implemented"); //$NON-NLS-1$ } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#isRecursive(org.eclipse.emf.ecore.EObject) */ @Override public boolean isRecursive(EObject theTreeNode) { checkNodeIsXmlDocumentEntity(theTreeNode); return ((theTreeNode instanceof XmlElement) && ((XmlElement)theTreeNode).isRecursive()); } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#isTreeRoot(org.eclipse.emf.ecore.EObject) */ @Override public boolean isTreeRoot(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); return (theTreeNode instanceof XmlFragment); } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#setTreeRoot(org.eclipse.emf.ecore.EObject) */ @Override public void setTreeRoot(EObject theFragment) { CoreArgCheck.isNotNull(theFragment); fragment = (XmlFragment) theFragment; // need to get all the transformations where the target is this fragment // each transformation will have a TreeMappingRoot for each MappingClass ModelContents modelContents = ModelerCore.getModelEditor().getModelContents(fragment); // mappingRoots = modelContents.getTransformations(fragment); } /* (non-Javadoc) * @See org.teiid.designer.mapping.factory.ITreeToRelationalMapper#isTreeRoot(org.eclipse.emf.ecore.EObject) */ @Override public boolean isTreeNode(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); return (theTreeNode instanceof XmlDocumentNode || isTreeRoot(theTreeNode) || theTreeNode instanceof XmlContainerNode ); } @Override public String getPathInDocument(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); if( theTreeNode instanceof XmlDocumentNode ) { return ((XmlDocumentNode)theTreeNode).getPathInDocument(); } if( theTreeNode instanceof XmlContainerNode ) { return ((XmlContainerNode)theTreeNode).getPathInDocument(); } return null; } @Override public String getXsdQualifiedName(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); if( theTreeNode instanceof XmlDocumentNode ) { XSDComponent xsdComponent = ((XmlDocumentNode)theTreeNode).getXsdComponent(); if ( xsdComponent != null && xsdComponent instanceof XSDNamedComponent ) { return ((XSDNamedComponent)xsdComponent).getQName(); } } return null; } @Override public String getXsdTargetNamespace(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); if( theTreeNode instanceof XmlDocumentNode ) { XSDComponent xsdComponent = ((XmlDocumentNode)theTreeNode).getXsdComponent(); if ( xsdComponent != null && xsdComponent instanceof XSDNamedComponent ) { return ((XSDNamedComponent)xsdComponent).getTargetNamespace(); } } return null; } /** * @see org.teiid.designer.mapping.factory.ITreeToRelationalMapper#getXsdComponent(org.eclipse.emf.ecore.EObject) * @since 4.3 */ @Override public EObject getXsdComponent(EObject theTreeNode) { CoreArgCheck.isNotNull(theTreeNode); if( theTreeNode instanceof XmlDocumentNode ) { EObject xsdComp = ((XmlDocumentNode)theTreeNode).getXsdComponent(); EObject container = xsdComp.eContainer(); if( container instanceof XSDParticle || container instanceof XSDAttributeUse) { return container; } return xsdComp; } else if( theTreeNode instanceof XmlContainerNode ) { return ((XmlContainerNode)theTreeNode).getXsdComponent(); } return null; } @Override public boolean isChoiceNode(EObject theTreeNode) { return theTreeNode instanceof XmlChoice; } }