/* * 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.metamodels.xml; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Stack; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.xsd.XSDAttributeDeclaration; import org.eclipse.xsd.XSDAttributeGroupDefinition; import org.eclipse.xsd.XSDAttributeUse; import org.eclipse.xsd.XSDAttributeUseCategory; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDComponent; import org.eclipse.xsd.XSDCompositor; import org.eclipse.xsd.XSDConstraint; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDFeature; import org.eclipse.xsd.XSDForm; import org.eclipse.xsd.XSDModelGroup; import org.eclipse.xsd.XSDModelGroupDefinition; import org.eclipse.xsd.XSDNamedComponent; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.impl.XSDComplexTypeDefinitionImpl; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.id.ObjectID; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.ModelEditor; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.validation.rules.StringNameValidator; import org.teiid.designer.metamodels.xml.util.XmlDocumentUtil; /** * XmlDocumentBuilderImpl * * @since 8.0 */ public class XmlDocumentBuilderImpl implements XmlDocumentBuilder { // private static final int MONITOR_MOD_COUNT = 100; private static final String TASK_NAME = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.buildingXmlDocForRoot"); //$NON-NLS-1$ private static final String CALCULATING_MSG = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Calculating_effort_to_build__1"); //$NON-NLS-1$ private static final String CALCULATING_MSG2 = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Calculating_effort_to_update__2"); //$NON-NLS-1$ private static final String BUILDING_MSG = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Building_Document_node__3"); //$NON-NLS-1$ private static final String UPDATING_MSG = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Updating_Document_node__4"); //$NON-NLS-1$ // private static final int MAX_COUNT = 50000; private final Collection schemaUris = new ArrayList(); private final Collection referencedSchemas = new ArrayList(); private final HashMap updateDeleteMap = new HashMap(); private final HashMap updateCurrentChildMap = new HashMap(); private final HashMap fragmentMap = new HashMap(); private final HashMap uriToNamespaceMap = new HashMap(); private final StringNameValidator validator = new StringNameValidator(); private final HashSet unhandledModelImports = new HashSet(); private final Collection recursiveElementUUIDs = new HashSet(); private int nodeCount; private int newCount; private int moveCount; private int factor; private int tempFactor; private Stack recursionStack; private int numberOfLevelsToBuild = -1; private XmlElement root; private IProgressMonitor monitor; private final XmlDocumentFactory docFactory = XmlDocumentFactory.eINSTANCE; private boolean useFragments; /** * Construct an instance of XmlDocumentBuilderImpl. */ public XmlDocumentBuilderImpl() { this(-1); } /** * Construct an instance of XmlDocumentBuilderImpl, passing in the number of levels to build * * @param the number of levels deep to build. */ public XmlDocumentBuilderImpl( final int levels ) { setNumberOfLevelsToBuild(levels); } private ModelEditor getModelEditor() { return ModelerCore.getModelEditor(); } /** * Recursively adds children of the given Schema Node to the given Document Node * * @param docParent * @param xsdComponent * @param taskName */ private void addChildren( final XmlDocumentEntity docParent, XSDComponent xsdComponent, final String taskName ) { // Add appropriate namespace on document element if corresponding XSD component is global or has form = qualified if (docParent instanceof XmlElement) { final XmlElement elem = (XmlElement)docParent; final XSDComponent parentXsdComp = elem.getXsdComponent(); if (parentXsdComp instanceof XSDFeature) { final XSDFeature feature = (XSDFeature)parentXsdComp; if (feature.isGlobal() || XSDForm.QUALIFIED_LITERAL.equals(feature.getForm())) { String uri = feature.getTargetNamespace(); //If the TNS is null in the XSDFeature, check the XSDComplexTypeDefintionImpl if (uri == null && xsdComponent != null && xsdComponent instanceof XSDComplexTypeDefinitionImpl) { uri = ((XSDComplexTypeDefinitionImpl)xsdComponent).getTargetNamespace(); } if (uri != null) { final XmlNamespace ns = (XmlNamespace)this.uriToNamespaceMap.get(uri); if (ns != null) elem.setNamespace(ns); } } } } // check numberOfLevelToBuild if (numberOfLevelsToBuild != -1) { final boolean proceed = checkDepth(docParent); if (!proceed) { setBuildStatusProperty((XmlBuildable)docParent, BuildStatus.INCOMPLETE_LITERAL); return; } } // Check the progressMonitor if (this.monitor.isCanceled()) return; // Convert any schema element ref or substitution group objects final XSDComponent ref = resolveSchemaRef(xsdComponent); // look for a reusable documentFragment if (useFragments) { final XmlFragment fragment = (ref == null ? (XmlFragment)fragmentMap.get(xsdComponent) : (XmlFragment)fragmentMap.get(ref)); if (fragment != null) { if (docParent instanceof XmlEntityHolder) { // create the XmlFragmentUse and set it's values from the xmlFragment final XmlFragmentUse use = docFactory.createXmlFragmentUse(); use.setFragment(fragment); use.setName(fragment.getName()); use.setXsdComponent(ref == null ? xsdComponent : ref); use.setNamespace(fragment.getRoot() == null ? null : fragment.getRoot().getNamespace()); use.setParent((XmlEntityHolder)docParent); // No need to process this node any further... just return; return; } if (ModelerCore.DEBUG_XML) System.out.println("Can't add docFragment to " + docParent.getClass().getName()); //$NON-NLS-1$ } } // init recursion variables boolean isRecursive = false; if (ref != null) { isRecursive = isRecursive(ref); recursionStack.push(ref); } else { isRecursive = isRecursive(xsdComponent); recursionStack.push(xsdComponent); } XmlDocumentEntity documentChild = docParent; if (xsdComponent instanceof XSDElementDeclaration && !((XSDElementDeclaration)xsdComponent).isAbstract()) { final XmlElement element = docFactory.createXmlElement(); element.setXsdComponent(xsdComponent); if (docParent instanceof XmlContainerNode) addValueToList(docParent, element, ((XmlContainerNode)docParent).getEntities()); else if (ModelerCore.DEBUG_XML) System.out.println("Can't add element to " + docParent.getClass().getName()); //$NON-NLS-1$ final XSDElementDeclaration xsdElement = (XSDElementDeclaration)xsdComponent; if (xsdElement.getLexicalValue() != null) { element.setValue(xsdElement.getLexicalValue()); final XSDConstraint constraint = xsdElement.getConstraint(); if (XSDConstraint.DEFAULT_LITERAL.equals(constraint)) element.setValueType(ValueType.DEFAULT_LITERAL); else if (XSDConstraint.FIXED_LITERAL.equals(constraint)) element.setValueType(ValueType.FIXED_LITERAL); } documentChild = element; if (ref != null) xsdComponent = ref; String name = ((XSDElementDeclaration)xsdComponent).getName(); if (ref != null) name = ((XSDElementDeclaration)ref).getName(); final String newName = this.validator.createValidName(name); if (newName != null) name = newName; element.setName(name); nodeCount++; updateMonitor(); } else if (xsdComponent instanceof XSDModelGroupDefinition || xsdComponent instanceof XSDAttributeGroupDefinition) { if (ref != null) xsdComponent = ref; updateMonitor(); } else if (xsdComponent instanceof XSDAttributeDeclaration) { // Determine if the attribute is prohibited ... final boolean prohibited = isProhibited((XSDAttributeDeclaration)xsdComponent); if (prohibited) // Remove any existing sibling attribute with the same name ... removeChildrenOfSameName(docParent, XSDAttributeDeclaration.class, ((XSDAttributeDeclaration)xsdComponent).getName()); else { final XmlAttribute attribute = docFactory.createXmlAttribute(); attribute.setXsdComponent(xsdComponent); if (docParent instanceof XmlElement) addValueToList(docParent, attribute, ((XmlElement)docParent).getAttributes()); else if (ModelerCore.DEBUG_XML) System.out.println("Can't add attribute to " + docParent.getClass().getName()); //$NON-NLS-1$ documentChild = attribute; if (ref != null) xsdComponent = ref; String name = ((XSDAttributeDeclaration)xsdComponent).getName(); if (ref != null) name = ((XSDAttributeDeclaration)ref).getName(); final String newName = this.validator.createValidName(name); if (newName != null) name = newName; attribute.setName(name); nodeCount++; updateMonitor(); } } else if (isAllCompositor(xsdComponent)) { XmlAll temp = null; // Attempt to find an existing all (this occurs when the complex type that uses // an all extends another complex type that has an all); final Iterator iter = docParent.eContents().iterator(); while (iter.hasNext()) { final Object sibling = iter.next(); if (sibling instanceof XmlAll) { temp = (XmlAll)sibling; // use this all component .. break; } } // If no all sibling found, then create one ... if (temp == null) { temp = docFactory.createXmlAll(); // ((XmlEntityHolder)docParent).getContainers().add(temp); addValueToList(docParent, temp, ((XmlEntityHolder)docParent).getEntities()); if (ref != null) xsdComponent = ref; nodeCount++; updateMonitor(); } temp.setXsdComponent(xsdComponent); documentChild = temp; } else if (isChoiceCompositor(xsdComponent)) { final XmlChoice choice = docFactory.createXmlChoice(); choice.setXsdComponent(xsdComponent); // ((XmlEntityHolder)docParent).getContainers().add(choice); addValueToList(docParent, choice, ((XmlEntityHolder)docParent).getEntities()); documentChild = choice; nodeCount++; updateMonitor(); } else if (isSequenceCompositor(xsdComponent)) { XmlSequence temp = null; // If the current doc node is a sequence, just use this instead of creating a new one (defect 8489) if (documentChild instanceof XmlSequence) if (isValidSequenceForReuse(((XmlSequence)documentChild).getXsdComponent(), xsdComponent)) temp = (XmlSequence)documentChild; if (temp == null) { // Attempt to find an existing sequence (this occurs when the complex type that uses // a sequence extends another complex type that has a sequence); final Iterator iter = docParent.eContents().iterator(); while (iter.hasNext()) { final Object sibling = iter.next(); if (sibling instanceof XmlSequence) // check the min / max occurs the the schema references // if they match then use this sequence if (isValidSequenceForReuse(((XmlSequence)sibling).getXsdComponent(), xsdComponent)) { temp = (XmlSequence)sibling; // use this sequence .. documentChild = temp; break; } } // If no reusable sequence found, then create one ... if (temp == null) { documentChild = docFactory.createXmlSequence(); // ((XmlEntityHolder)docParent).getContainers().add(documentChild); addValueToList(docParent, documentChild, ((XmlEntityHolder)docParent).getEntities()); nodeCount++; updateMonitor(); } ((XmlSequence)documentChild).setXsdComponent(xsdComponent); } } // debug else if (ModelerCore.DEBUG_XML) System.out.println("Found unexpected " + xsdComponent.getClass().getName()); //$NON-NLS-1$ final ObjectID uuid = ModelerCore.getObjectId(documentChild); if (isRecursive) { if (documentChild instanceof XmlElement) { setRecursiveProperty((XmlElement)documentChild, true); setBuildStatusProperty((XmlElement)documentChild, BuildStatus.INCOMPLETE_LITERAL); } if (recursiveElementUUIDs.contains(uuid)) { if (!recursionStack.isEmpty()) recursionStack.pop(); return; } recursiveElementUUIDs.add(uuid); if (!recursionStack.isEmpty()) recursionStack.pop(); return; } // Add namespace for XSDComponent if required if (documentChild != docParent) { updateSchemaUris(documentChild, xsdComponent); // Force GC if (nodeCount % 10000 == 0) { System.gc(); Thread.yield(); } } // The element and attributes in the schema should have a type that is either // a SimpleType or ComplexType; use that and build child objects according to the type // However, DO NOT do this if the type is an anonymous type below the schema element, // since children are handled below! final XSDComponent typeEntity = XmlDocumentUtil.findXSDType(xsdComponent); if (typeEntity != null && typeEntity.eContainer() != null && !typeEntity.eContainer().equals(xsdComponent)) { addChildren(documentChild, typeEntity, taskName); // re-check recursive status in case above call incorrectly set it: if (xsdComponent instanceof XSDComplexTypeDefinition && isRecursive(typeEntity)) // xsdComponent is a complex type and // it is not recursive (see earlier // isRecursive check). // Its super type is recursive, but only exact matches count. setRecursiveProperty((XmlElement)documentChild, false); } // make sure we are not marked recursive: if (!recursiveElementUUIDs.contains(uuid)) { // Now add this Node's Children (like attributes) to the new Node (or this node if the schema element was not valid) final Iterator children = xsdComponent.eContents().iterator(); while (children.hasNext()) { final Object child = children.next(); if (child instanceof XSDComponent) addChildren(documentChild, (XSDComponent)child, taskName); } // enwhile } // endif if (!recursionStack.isEmpty()) recursionStack.pop(); } /** * This method insures that all ancestor elements are added to the recursion stack. */ private void addRecursionParentTypes( final XmlBaseElement node ) { // process parent first: final XmlEntityHolder parent = node.getParent(); if (parent instanceof XmlContainerNode) { // keep searching parents until we get another base element: XmlEntityHolder nextParent = ((XmlContainerNode)parent).getParent(); while (nextParent != null) { if (nextParent instanceof XmlElement) { addRecursionParentTypes((XmlElement)nextParent); break; // we found a parent, don't need to process any further. } // endif nextParent = ((XmlContainerNode)parent).getParent(); } // endwhile } // endif // process the type: final XSDComponent schemaComponent = node.getXsdComponent(); // Convert any schema element ref objects final XSDComponent ref = resolveSchemaRef(schemaComponent); // add to stack: if (ref != null) recursionStack.push(ref); else recursionStack.push(schemaComponent); } private void addValueToList( final Object owner, final Object value, final EList feature ) { try { ModelerCore.getModelEditor().addValue(owner, value, feature); } catch (final ModelerCoreException err) { ModelerCore.Util.log(IStatus.ERROR, err, XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Error_adding_object", value)); //$NON-NLS-1$ } } /* (non-Javadoc) * @see org.teiid.designer.metamodels.xml.XmlDocumentBuilder#buildDocument(org.teiid.designer.metamodels.xml.XmlElement, org.eclipse.core.runtime.IProgressMonitor) */ @Override public int buildDocument( final XmlElement rootElement, final IProgressMonitor progressMonitor ) throws ModelerCoreException { CoreArgCheck.isNotNull(rootElement); this.root = rootElement; this.monitor = progressMonitor != null ? progressMonitor : new NullProgressMonitor(); // reset the attributes for tracking namespaces referencedSchemas.clear(); initializeSchemaUris(rootElement); // Start UoW if neccessary final boolean startedTxn = ModelerCore.startTxn(true, true, XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Build_XML_Document_5"), this); //$NON-NLS-1$ boolean succeeded = false; try { // update flags: setBuildStatusProperty(rootElement, BuildStatus.COMPLETE_LITERAL); if (rootElement.isRecursive()) setRecursiveProperty(rootElement, false); // Resolve the Schema definition for the root object final XSDComponent schemaComponent = root.getXsdComponent(); if (schemaComponent == null) throw new ModelerCoreException( XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Schema_reference_not_set_on_root_element_1")); //$NON-NLS-1$ // The root Element in the schema should have a type that is either // a SimpleType or ComplexType final XSDTypeDefinition typeDefn = XmlDocumentUtil.findXSDType(schemaComponent); if (typeDefn != null) { // Ensure name matches if (schemaComponent instanceof XSDNamedComponent) { String name = ((XSDNamedComponent)schemaComponent).getName(); // if the element is not named get it from the type if ((name == null) || (name.length() == 0)) { final XSDComponent ref = resolveSchemaRef(schemaComponent); if ((ref != null) && (ref instanceof XSDNamedComponent)) name = ((XSDNamedComponent)ref).getName(); } final String newName = this.validator.createValidName(name); if (newName != null) name = newName; root.setName(name); } // Set the units of work on the monitor monitor.beginTask(TASK_NAME, 5000); monitor.subTask(CALCULATING_MSG + root.getName()); initializeRecursionStack(root); // countNodes(typeDefn, CALCULATING_MSG + root.getName(), false); // monitor.worked(500); monitor.subTask(BUILDING_MSG + root.getName()); // if(ModelerCore.DEBUG_XML){ // System.out.println("Done Counting : " + nodeCount); //$NON-NLS-1$ // } // // estimatedNodeCount = nodeCount; // //Calculate factor to use when adding work for each remaining node. // final double temp = 4500d / nodeCount; // if(temp > 0){ // this.factor = 4500 / nodeCount; // }else{ // this.factor = new Double(0 - (1/(4500d/9200) ) ).intValue(); // } // initializeRecursionStack(root); nodeCount = 0; // Recursively build document from schema addChildren(this.root, typeDefn, BUILDING_MSG + root.getName()); } else throw new ModelerCoreException( XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.No_type_element_found_for_root_document_element_2")); //$NON-NLS-1$ succeeded = true; return nodeCount; } catch (final ModelerCoreException e) { // Just throw MCE throw e; } catch (final Throwable e) { // This is an unexpected e... ensure that it is logged ModelerCore.Util.log(IStatus.ERROR, e, e.getMessage()); } finally { // cleanup cleanup(); // Commit txn if we started it. if (startedTxn) if (succeeded) ModelerCore.commitTxn(); else ModelerCore.rollbackTxn(); // Force GC System.gc(); Thread.yield(); monitor.worked(10); } return nodeCount; } /* (non-Javadoc) * @see org.teiid.designer.metamodels.xml.XmlDocumentBuilder#buildXmlFragments(org.eclipse.xsd.XSDSchema) */ @Override public Collection buildXmlFragments( final XSDSchema schema ) throws ModelerCoreException { CoreArgCheck.isNotNull(schema); final Collection fragments = new ArrayList(); final Iterator iter = schema.getContents().iterator(); while (iter.hasNext()) { if (this.monitor != null && this.monitor.isCanceled()) break; final Object next = iter.next(); if (next instanceof XSDElementDeclaration) { final XSDElementDeclaration child = (XSDElementDeclaration)next; final XmlRoot root = docFactory.createXmlRoot(); final XmlDocumentBuilder builder = new XmlDocumentBuilderImpl(); root.setXsdComponent(child); builder.buildDocument(root, null); final XmlFragment fragment = docFactory.createXmlFragment(); String name = root.getName(); final String newName = this.validator.createValidName(name); if (newName != null) name = newName; fragment.setName(name); fragment.setRoot(root); fragments.add(fragment); } } return fragments; } /** * return true if depth OK */ private boolean checkDepth( final EObject child ) { final boolean isCompositor = child instanceof XmlAll || child instanceof XmlChoice || child instanceof XmlSequence; if (isCompositor || child == this.root) return true; int depth = 1; EObject parent = child.eContainer(); while (parent != null && parent != this.root) { parent = parent.eContainer(); depth++; } return depth < numberOfLevelsToBuild; } private void cleanup() { useFragments = false; numberOfLevelsToBuild = -1; numberOfLevelsToBuild = -1; root = null; if (recursionStack != null) recursionStack.clear(); recursiveElementUUIDs.clear(); updateDeleteMap.clear(); updateCurrentChildMap.clear(); uriToNamespaceMap.clear(); } /** * Find an existing child in the document that is valid for reuse * * @param documentChildren - collection of existing document children * @param xsdComponent * @return matching docChild or null if none found */ private XmlDocumentEntity findExistingDocumentChild( final ArrayList documentChildren, final XSDComponent xsdComponent ) { if (documentChildren == null || documentChildren.isEmpty()) return null; Iterator children = documentChildren.iterator(); while (children.hasNext()) { final XmlDocumentEntity next = (XmlDocumentEntity)children.next(); if (xsdComponent.equals(findSchemaReference(next))) return next; } // If no exact match found and the schema reference is a sequence or an all // look for a re-useable seqence or all. if (isAllCompositor(xsdComponent) || isSequenceCompositor(xsdComponent)) { children = documentChildren.iterator(); while (children.hasNext()) { final XmlDocumentEntity child = (XmlDocumentEntity)children.next(); final XSDComponent schemaRef = findSchemaReference(child); if (schemaRef != null) if (xsdComponent.eClass().equals(schemaRef.eClass())) { final Iterator grandchildren = child.eContents().iterator(); while (grandchildren.hasNext()) { final XmlDocumentEntity grandchild = (XmlDocumentEntity)grandchildren.next(); final XSDComponent gcSchemaRef = findSchemaReference(grandchild); if (xsdComponent.eContents().contains(gcSchemaRef)) return child; } } } } return null; } /** * Return the XsdComponent for the given XmlDocumentEntity - or null if none exists * * @param child * @return the XsdComponent for the given XmlDocumentEntity - or null if none exists */ private XSDComponent findSchemaReference( final EObject child ) { if (child instanceof XmlDocumentNode) return ((XmlDocumentNode)child).getXsdComponent(); else if (child instanceof XmlContainerNode) return ((XmlContainerNode)child).getXsdComponent(); return null; } /** * Derive the index of the child within the containment feature of the given parent * * @param parent * @param child * @return -1 if the child is not a child of the given parent, else return the index of the child in the parent's containment * feature. */ private int getIndexOfChild( final EObject parent, final EObject child ) { if (child.eContainer() == null || !child.eContainer().equals(parent)) return -1; // If the parent is a XSDParticle, you really want the index of the Particle in it's ModelGroup. if (parent instanceof XSDParticle || parent instanceof XSDAttributeUse) return getIndexOfChild(parent.eContainer(), parent); final EStructuralFeature sf = child.eContainmentFeature(); if (sf == null) return -1; final Object result = parent.eGet(sf); if (result == null || !(result instanceof EList)) return -1; return ((EList)result).indexOf(child); } /** * Returns the collection of ObjectID objects for any recursive nodes in the tree * * @return Colection of ObjectIDs... never null */ @Override public Collection getRecursiveElementObjectIDs() { return recursiveElementUUIDs; } public Collection getUnhandledModelImports() { return this.unhandledModelImports; } /** * @param node the root node to work from */ private void initializeRecursionStack( final XmlElement root ) { recursionStack = new Stack(); addRecursionParentTypes(root); } // ------------------------------------------------------------------------- // I N S T A N C E M E T H O D S // ------------------------------------------------------------------------- /** * Initialize the schemaUris attribute, adding an entry for each namespace found at the given element's document * * @param rootElement */ private void initializeSchemaUris( final XmlElement element ) { final XSDComponent xsdComp = element.getXsdComponent(); // If there is not xsd component for this element, just return if (xsdComp == null) return; // find the root element XmlElement root = null; EObject node = element; while (root == null && node != null) if (node instanceof XmlRoot) root = (XmlElement)node; else node = node.eContainer(); // If we can't find a doc parent... just return; if (root == null) return; // Add the uri for each namespace child of the document final Iterator children = root.eContents().iterator(); while (children.hasNext()) { final EObject next = (EObject)children.next(); if (next instanceof XmlNamespace) schemaUris.add(((XmlNamespace)next).getUri()); } // Update the list for the given xsd component updateSchemaUris(element, xsdComp); } private boolean isAllCompositor( final XSDComponent xsdComponent ) { if (xsdComponent instanceof XSDModelGroup) return (((XSDModelGroup)xsdComponent).getCompositor().getValue() == XSDCompositor.ALL); return false; } private boolean isChoiceCompositor( final XSDComponent xsdComponent ) { if (xsdComponent instanceof XSDModelGroup) return (((XSDModelGroup)xsdComponent).getCompositor().getValue() == XSDCompositor.CHOICE); return false; } private boolean isProhibited( final XSDAttributeDeclaration attribute ) { CoreArgCheck.isNotNull(attribute); final XSDAttributeUse attUse = (XSDAttributeUse)attribute.eContainer(); if (attUse == null) return false; final XSDAttributeUseCategory cat = attUse.getUse(); if (cat == null) return false; final int val = cat.getValue(); return val == XSDAttributeUseCategory.PROHIBITED; } /** * @param xsdComponent * @return */ private boolean isRecursive( final XSDComponent xsdComponent ) { return this.recursionStack.contains(xsdComponent); } private boolean isSequenceCompositor( final XSDComponent xsdComponent ) { if (xsdComponent instanceof XSDModelGroup) return (((XSDModelGroup)xsdComponent).getCompositor().getValue() == XSDCompositor.SEQUENCE); return false; } /** * Determine if a sequence if valid for reuse. A sequence may be reused if the min / max occurs are the same for it's parent and * the potential resuse parent */ private boolean isValidSequenceForReuse( final EObject sequence1, final EObject sequence2 ) { if (sequence1 == null || sequence2 == null) return false; if (sequence1 instanceof XSDModelGroup && sequence2 instanceof XSDModelGroup) { final Object obj1 = sequence1.eContainer(); final Object obj2 = sequence2.eContainer(); boolean sameMult = false; if (obj1 instanceof XSDParticle && obj2 instanceof XSDParticle) { final XSDParticle p1 = (XSDParticle)sequence1.eContainer(); final XSDParticle p2 = (XSDParticle)sequence2.eContainer(); if (p1 != null && p2 != null) { final int max1 = p1.getMaxOccurs(); final int max2 = p2.getMaxOccurs(); final boolean maxMatch = max1 == max2; final int min1 = p1.getMinOccurs(); final int min2 = p2.getMinOccurs(); final boolean minMatch = min1 == min2; sameMult = minMatch && maxMatch; } // Case 3171: Do not reuse sequence if they are both a child of the same // choice node. boolean isChoice = false; if (sameMult) { final EObject parent1 = p1.eContainer(); final EObject parent2 = p2.eContainer(); if (parent1 == parent2 && parent1 != null && parent1 instanceof XSDModelGroup) isChoice = ((XSDModelGroup)parent1).getCompositor().equals(XSDCompositor.CHOICE_LITERAL); if (isChoice) return false; } return sameMult; } } return false; } /** * Count the buildable nodes from the given schemaComponent * * @param schemaComponent * @param string - task name * @param done */ // private void countNodes(final XSDComponent schemaComponent, final String taskName, final boolean done) { // if(ModelerCore.DEBUG_XML){ // if(schemaComponent instanceof XSDNamedComponent){ // System.out.println("Counting " + ((XSDNamedComponent)schemaComponent).getName() ); //$NON-NLS-1$ // }else{ // System.out.println("Counting " + schemaComponent); //$NON-NLS-1$ // } // } // // if(nodeCount >= MAX_COUNT) { // return; // } // // boolean isRecursive = recursionStack.contains(schemaComponent); // boolean checkRecursion = false; // recursionStack.push(schemaComponent); // // //Don't count when building steps // if(numberOfLevelsToBuild > -1){ // nodeCount = 50; // return; // } // // //Check the progressMonitor // if(this.monitor.isCanceled() ){ // return; // } // // //find resolvable schema ref for the given node // final XSDComponent ref = resolveSchemaRef(schemaComponent); // if(ref != null){ // countNodes(ref, taskName, done); // if(!recursionStack.isEmpty() ) { // recursionStack.pop(); // System.gc(); // Thread.yield(); // } // return; // } // // // // //First add this node if it is an Element, Attribute, Namespace or a Comment // if(schemaComponent instanceof XSDElementDeclaration){ // nodeCount++; // checkRecursion = true; // if (nodeCount % MONITOR_MOD_COUNT == 0) { // monitor.subTask(XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.{0}___{1}_elements_found_7", taskName, new Integer(nodeCount) )); //$NON-NLS-1$ // updateMonitor(); // } // endif // }else if(schemaComponent instanceof XSDAttributeDeclaration){ // nodeCount++; // if (nodeCount % MONITOR_MOD_COUNT == 0) { // monitor.subTask(XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.{0}___{1}_elements_found_7", taskName, new Integer(nodeCount) )); //$NON-NLS-1$ // updateMonitor(); // } // endif // }else if(schemaComponent instanceof XSDParticle){ // nodeCount++; // checkRecursion = true; // if (nodeCount % MONITOR_MOD_COUNT == 0) { // monitor.subTask(XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.{0}___{1}_elements_found_7", taskName, new Integer(nodeCount) )); //$NON-NLS-1$ // updateMonitor(); // } // endif // }else if(schemaComponent instanceof XSDModelGroup ){ // nodeCount++; // checkRecursion = isAllCompositor(schemaComponent); // if (nodeCount % MONITOR_MOD_COUNT == 0) { // monitor.subTask(XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.{0}___{1}_elements_found_7", taskName, new Integer(nodeCount) )); //$NON-NLS-1$ // updateMonitor(); // } // endif // } // // // //Check for recursion // if(isRecursive && checkRecursion){ // if(!recursionStack.isEmpty() ) { // recursionStack.pop(); // System.gc(); // Thread.yield(); // } // return; // } // // // // //Now add this Node's Children (like attributes) to the new Node (or this node if the schema element was not valid) // final Iterator children = schemaComponent.eContents().iterator(); // while(children.hasNext() ){ // final Object child = children.next(); // if(child instanceof XSDComponent){ // countNodes((XSDComponent)child, taskName, done); // } // } // // // The element and attributes in the schema should have a type that is either // // a SimpleType or ComplexType; use that and build child objects according to the type // // However, DO NOT do this if the type is an anonymous type below the schema element, // // since children were handled in the previous block! // final XSDComponent typeComponent = XmlDocumentUtil.findXSDType(schemaComponent); // if ( typeComponent != null && typeComponent.eContainer() != null && !typeComponent.eContainer().equals(schemaComponent) ) { // countNodes(typeComponent, taskName, done); // } // // if(!recursionStack.isEmpty() ) { // recursionStack.pop(); // System.gc(); // Thread.yield(); // } // } /** * Move the existing child to the appropriate index in the docParent so that it is consistent with the schema * * @param documentElement * @param xsdComponent * @return true if document element was moved. */ private boolean moveIfNotAtSameIndex( final XmlDocumentEntity documentElement, final XSDComponent xsdComponent ) throws ModelerCoreException { boolean moved = false; final XmlDocumentEntity docParent = (XmlDocumentEntity)documentElement.eContainer(); final XSDComponent schemaParent = (XSDComponent)xsdComponent.eContainer(); final int docIndex = getIndexOfChild(docParent, documentElement); final int schemaIndex = getIndexOfChild(schemaParent, xsdComponent); if (docIndex == -1) throw new ModelerCoreException( XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Unable_to_find_document_index_for_{0}_5") + xsdComponent.toString()); //$NON-NLS-1$ else if (schemaIndex == -1) throw new ModelerCoreException( XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Unable_to_find_schema_index_for_{0}_6") + documentElement.toString()); //$NON-NLS-1$ // If the documentElement and schemaElement are both at index 0 of their parent no move required if (docIndex == 0 && schemaIndex == 0) return moved; // If both index are not == 0 ensure that the index of the docElement is in the same relative // place as it's schemaElement final EList docList = (EList)docParent.eGet(documentElement.eContainmentFeature()); EList schemaList = null; // If the schemaParent is a Particle, you really want the list from the ModelGroup. if (schemaParent instanceof XSDParticle || schemaParent instanceof XSDAttributeUse) schemaList = (EList)schemaParent.eContainer().eGet(schemaParent.eContainmentFeature()); else schemaList = (EList)schemaParent.eGet(xsdComponent.eContainmentFeature()); // Easy Case... schemaIndex > 0 // If schemaIndex > 0 and docParent.getChildCount() > 1 move may be required if (schemaIndex > 0 && docList.size() > 1) { final EObject tempSchemaElement = (EObject)schemaList.get((schemaIndex - 1)); final Iterator docChildren = docList.iterator(); boolean found = false; while (docChildren.hasNext() && !found) { final EObject docChild = (EObject)docChildren.next(); final EObject schemaRef = findSchemaReference(docChild); if (schemaRef != null && schemaRef == tempSchemaElement) { found = true; final int temp = getIndexOfChild(docParent, docChild) + 1; if (temp != docIndex) { getModelEditor().move(docParent, documentElement, temp); moved = true; } } } } else if (schemaIndex == 0 && docList.size() > 1) { // Ensure that no schemaElement siblings are ahead of this element in doc int foundIndex = Integer.MAX_VALUE; final Iterator docChildren = docList.iterator(); while (docChildren.hasNext()) { final EObject docChild = (EObject)docChildren.next(); final EObject schemaRef = findSchemaReference(docChild); if (schemaRef != null && schemaRef != xsdComponent && schemaList.contains(schemaRef)) { final int temp = getIndexOfChild(docParent, docChild); if (temp < foundIndex) foundIndex = temp; } } if (foundIndex < Integer.MAX_VALUE && foundIndex != docIndex) { getModelEditor().move(docParent, documentElement, foundIndex); moved = true; } } return moved; } /** * Perform deletes from the updateDeleteMap * * @return number of deleted nodes. */ private int performTreeUpdates() throws ModelerCoreException { int count = 0; // Perform the deletes final Iterator keys = updateDeleteMap.keySet().iterator(); while (keys.hasNext()) { final EObject parent = (EObject)keys.next(); final ArrayList children = (ArrayList)updateDeleteMap.get(parent); if (children != null) { final Iterator childrenIT = children.iterator(); while (childrenIT.hasNext()) { final EObject child = (EObject)childrenIT.next(); getModelEditor().delete(child); count++; } } } return count; } private void removeChildrenOfSameName( final EObject parent, final Class clazz, String name ) { final String newName = this.validator.createValidName(name); if (newName != null) name = newName; final Iterator children = parent.eContents().iterator(); while (children.hasNext()) { final EObject next = (EObject)children.next(); if (clazz.isAssignableFrom(next.getClass())) try { final String name2 = ((XmlDocumentNode)next).getName(); if (name == null && name2 == null) getModelEditor().delete(next); else if (name != null && name.equals(name2)) getModelEditor().delete(next); } catch (final ModelerCoreException e) { ModelerCore.Util.log(IStatus.ERROR, e, XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Error_removing_children_of_same_name_1")); //$NON-NLS-1$ } } } /** * Return the resolvable schema ref for the given schemaComponent - may be null * * @param schemaComponent * @return the resolvable schema ref for the given schemaComponent - may be null */ private XSDComponent resolveSchemaRef( final XSDComponent schemaComponent ) { XSDComponent ref = null; if (schemaComponent instanceof XSDElementDeclaration) ref = ((XSDElementDeclaration)schemaComponent).getResolvedElementDeclaration(); else if (schemaComponent instanceof XSDModelGroupDefinition) ref = ((XSDModelGroupDefinition)schemaComponent).getResolvedModelGroupDefinition(); else if (schemaComponent instanceof XSDAttributeDeclaration) ref = ((XSDAttributeDeclaration)schemaComponent).getResolvedAttributeDeclaration(); else if (schemaComponent instanceof XSDAttributeGroupDefinition) ref = ((XSDAttributeGroupDefinition)schemaComponent).getResolvedAttributeGroupDefinition(); if (ref == null || ref == schemaComponent) return null; return ref; } /** * Setter for the addNamespaces attribute. If true, namespaces will automatically be added at the document level for each schema * referenced in the build process. Default is true, attribute only needs to be set if no namespaces are desired * * @param addNamespaces */ @Override public void setAddNamespaces( final boolean addNamespaces ) { } private void setBuildStatusProperty( final XmlBuildable xb, final BuildStatus status ) { xb.setBuildState(status); } // ------------------------------------------------------------------------- // I N T E R F A C E M E T H O D S // ------------------------------------------------------------------------- /* (non-Javadoc) * @see org.teiid.designer.metamodels.xml.XmlDocumentBuilder#setFragments(java.util.Collection) */ @Override public void setFragments( final Collection xmlFragments ) throws ModelerCoreException { // Check arguements and initialize variables CoreArgCheck.isNotNull(xmlFragments); final Iterator iter = xmlFragments.iterator(); while (iter.hasNext()) { final Object next = iter.next(); if (!(next instanceof XmlFragment)) throw new ModelerCoreException( XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.All_xml_fragments_must_be_an_instance_of_XmlFragment_1")); //$NON-NLS-1$ // Create an XmlFragmentUse for each fragment and register it in the fragment map by xsdcomponent final XmlFragment fragment = (XmlFragment)next; XSDComponent xsdComponent = fragment.getRoot() == null ? null : fragment.getRoot().getXsdComponent(); if (xsdComponent != null) { // resolve the xsd component if it is a ref final XSDComponent ref = resolveSchemaRef(xsdComponent); if (ref != null) xsdComponent = ref; // Add the new new XmlFragment to the map with the xsdComponent as the key fragmentMap.put(xsdComponent, fragment); } } useFragments = !fragmentMap.isEmpty(); } /* (non-Javadoc) * @see org.teiid.designer.metamodels.xml.XmlDocumentBuilder#setNumberOfLevelsToBuild(int) */ @Override public void setNumberOfLevelsToBuild( final int numberOfLevelsToBuild ) { this.numberOfLevelsToBuild = numberOfLevelsToBuild; } private void setRecursiveProperty( final XmlElement element, final boolean recursive ) { element.setRecursive(recursive); final XSDComponent xsdComponent = element.getXsdComponent(); // now climb the tree and find the top recursive element EObject parent = element.eContainer(); while (parent != null) { if (parent instanceof XmlElement) if (((XmlElement)parent).getXsdComponent() == xsdComponent) { ((XmlElement)parent).setRecursive(recursive); return; } parent = parent.eContainer(); } } /** * Perform the adds for the updateFromSchema process * * @param documentElement * @param xsdComponent * @param string */ private void updateAdd( final XmlDocumentEntity documentElement, XSDComponent xsdComponent, final String taskName ) throws ModelerCoreException { // Check the progressMonitor if (this.monitor.isCanceled()) return; this.monitor.subTask(XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.{0}_{1}_of_{2}", taskName, new Integer(nodeCount), new Integer(4500))); //$NON-NLS-1$ // Convert any schema element ref or substitution group objects final XSDComponent ref = resolveSchemaRef(xsdComponent); // look for a reusable documentFragment if (useFragments) { final XmlFragment fragment = (ref == null ? (XmlFragment)fragmentMap.get(xsdComponent) : (XmlFragment)fragmentMap.get(ref)); if (fragment != null) { if (documentElement instanceof XmlEntityHolder) { // create the XmlFragmentUse and set it's values from the xmlFragment final XmlFragmentUse use = docFactory.createXmlFragmentUse(); use.setFragment(fragment); use.setName(fragment.getName()); use.setXsdComponent(ref == null ? xsdComponent : ref); use.setNamespace(fragment.getRoot() == null ? null : fragment.getRoot().getNamespace()); use.setParent((XmlEntityHolder)documentElement); // No need to process this node any further... just return; return; } if (ModelerCore.DEBUG_XML) System.out.println("Can't add docFragment to " + documentElement.getClass().getName()); //$NON-NLS-1$ } } // check for recursion and isExisting variables boolean checkRecursion = false; boolean isRecursive = false; boolean checkExisting = false; if (ref != null) { if (recursionStack.contains(ref)) isRecursive = true; recursionStack.push(ref); } else { if (recursionStack.contains(xsdComponent)) isRecursive = true; recursionStack.push(xsdComponent); } XmlDocumentEntity documentChild = documentElement; final ArrayList docChildren = new ArrayList(documentChild.eContents()); final XmlDocumentEntity existingChild = findExistingDocumentChild(docChildren, xsdComponent); if (xsdComponent instanceof XSDElementDeclaration && !((XSDElementDeclaration)xsdComponent).isAbstract()) { // Process only non-abstract XSDElements String name = ((XSDElementDeclaration)xsdComponent).getName(); if (ref != null) name = ((XSDElementDeclaration)ref).getName(); if (existingChild == null) { final XmlElement temp = docFactory.createXmlElement(); temp.setXsdComponent(xsdComponent); if (documentElement instanceof XmlContainerNode) addValueToList(documentElement, temp, ((XmlContainerNode)documentElement).getEntities()); else if (ModelerCore.DEBUG_XML) System.out.println("Can't add element to " + documentElement.getClass().getName()); //$NON-NLS-1$ final XSDElementDeclaration xsdElement = (XSDElementDeclaration)xsdComponent; if (xsdElement.getLexicalValue() != null) { temp.setValue(xsdElement.getLexicalValue()); final XSDConstraint constraint = xsdElement.getConstraint(); if (XSDConstraint.DEFAULT_LITERAL.equals(constraint)) temp.setValueType(ValueType.DEFAULT_LITERAL); else if (XSDConstraint.FIXED_LITERAL.equals(constraint)) temp.setValueType(ValueType.FIXED_LITERAL); } documentChild = temp; newCount++; } else { // Ensure index is the same final boolean moved = moveIfNotAtSameIndex(existingChild, xsdComponent); if (moved) moveCount++; // verify the name's match final String newName = this.validator.createValidName(name); if (newName != null) name = newName; if (!name.equals(((XmlElement)existingChild).getName())) ((XmlElement)existingChild).setName(name); documentChild = existingChild; } if (ref != null) xsdComponent = ref; checkRecursion = true; checkExisting = true; nodeCount++; updateMonitor(); } else if (xsdComponent instanceof XSDModelGroupDefinition) { if (ref != null) xsdComponent = ref; updateMonitor(); } else if (xsdComponent instanceof XSDAttributeDeclaration) { String name = ((XSDAttributeDeclaration)xsdComponent).getName(); if (ref != null) name = ((XSDAttributeDeclaration)ref).getName(); if (existingChild == null) { // Determine if the attribute is prohibited ... final boolean prohibited = isProhibited((XSDAttributeDeclaration)xsdComponent); if (prohibited) // Remove any existing sibling attribute with the same name ... removeChildrenOfSameName(documentElement, XSDAttributeDeclaration.class, ((XSDAttributeDeclaration)xsdComponent).getName()); else { final XmlAttribute attribute = docFactory.createXmlAttribute(); attribute.setXsdComponent(xsdComponent); if (documentElement instanceof XmlElement) // ((XmlElement)documentElement).getAttributes().add(attribute); addValueToList(documentElement, attribute, ((XmlElement)documentElement).getAttributes()); else if (ModelerCore.DEBUG_XML) System.out.println("Can't add attribute to " + documentElement.getClass().getName()); //$NON-NLS-1$ documentChild = attribute; newCount++; } } else { // Ensure index is the same final boolean moved = moveIfNotAtSameIndex(existingChild, xsdComponent); if (moved) moveCount++; // verify the name's match final String newName = this.validator.createValidName(name); if (newName != null) name = newName; if (!name.equals(((XmlAttribute)existingChild).getName())) ((XmlAttribute)existingChild).setName(name); documentChild = existingChild; } if (ref != null) xsdComponent = ref; checkExisting = true; nodeCount++; updateMonitor(); } else if (isAllCompositor(xsdComponent)) { XmlAll temp = null; // Attempt to find an existing all (this occurs when the complex type that uses // an all extends another complex type that has an all); final Iterator iter = documentElement.eContents().iterator(); while (iter.hasNext()) { final XmlAll sibling = (XmlAll)iter.next(); temp = sibling; temp.setXsdComponent(xsdComponent); documentChild = sibling; // use this all component .. break; } // If no all sibling found, create one ... if (temp == null) { temp = docFactory.createXmlAll(); temp.setXsdComponent(xsdComponent); // ((XmlEntityHolder)documentElement).getContainers().add(temp); addValueToList(documentElement, temp, ((XmlEntityHolder)documentElement).getEntities()); documentChild = temp; newCount++; } if (ref != null) xsdComponent = ref; checkRecursion = true; nodeCount++; updateMonitor(); } else if (isChoiceCompositor(xsdComponent)) { if (existingChild == null) { final XmlChoice temp = docFactory.createXmlChoice(); temp.setXsdComponent(xsdComponent); // ((XmlEntityHolder)documentElement).getContainers().add(temp); addValueToList(documentElement, temp, ((XmlEntityHolder)documentElement).getEntities()); documentChild = temp; newCount++; } else documentChild = existingChild; checkExisting = true; nodeCount++; updateMonitor(); } else if (isSequenceCompositor(xsdComponent)) { XmlSequence temp = null; // Attempt to find an existing sequence (this occurs when the complex type that uses // a sequence extends another complex type that has a sequence); final Iterator iter = documentElement.eContents().iterator(); while (iter.hasNext()) { final EObject sibling = (EObject)iter.next(); if (sibling instanceof XmlSequence) // check the min / max occurs the the schema references // if they match then use this sequence if (isValidSequenceForReuse(findSchemaReference(sibling), xsdComponent)) { temp = (XmlSequence)sibling; // use this sequence .. documentChild = temp; break; } } // If no sequence sibling found, create one ... if (temp == null) { temp = docFactory.createXmlSequence(); // ((XmlEntityHolder)documentElement).getContainers().add(temp); addValueToList(documentElement, temp, ((XmlEntityHolder)documentElement).getEntities()); documentChild = temp; newCount++; } temp.setXsdComponent(xsdComponent); nodeCount++; updateMonitor(); } if (isRecursive && checkRecursion) { // break on recursion if (documentChild instanceof XmlElement) ((XmlElement)documentChild).setRecursive(true); final ObjectID uuid = ModelerCore.getObjectId(documentChild); if (recursiveElementUUIDs.contains(uuid)) { if (!recursionStack.isEmpty()) recursionStack.pop(); return; } recursiveElementUUIDs.add(uuid); if (!recursionStack.isEmpty()) recursionStack.pop(); return; } // Add namespace for XSDComponent if required if (documentChild != documentElement) updateSchemaUris(documentChild, xsdComponent); // The element and attributes in the schema should have a type that is either // a SimpleType or ComplexType; use that and build child objects according to the type // However, DO NOT do this if the type is an anonymous type below the schema element, // since children are handled below! final XSDComponent typeEntity = XmlDocumentUtil.findXSDType(xsdComponent); if (typeEntity != null && typeEntity.eContainer() != null && !typeEntity.eContainer().equals(xsdComponent)) if (checkExisting && existingChild == null) addChildren(documentChild, typeEntity, taskName); else updateAdd(documentChild, typeEntity, taskName); // Now add this Node's Children (like attributes) to the new Node (or this node if the schema element was not valid) final Iterator children = xsdComponent.eContents().iterator(); while (children.hasNext()) { final EObject schemaChild = (EObject)children.next(); // may have XSDDiagnostics that are not XSDComponents! if (schemaChild instanceof XSDComponent) if (checkExisting && existingChild == null) addChildren(documentChild, (XSDComponent)schemaChild, taskName); else updateAdd(documentChild, (XSDComponent)schemaChild, taskName); } if (!recursionStack.isEmpty()) recursionStack.pop(); } /** * Perform deletes for the updateFromSchema process * * @param documentElement * @param xsdComponent * @param string task name */ private void updateDelete( final XmlDocumentEntity documentElement, XSDComponent xsdComponent, final String taskName ) { // Check the progressMonitor if (this.monitor.isCanceled()) return; this.monitor.subTask(XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.{0}_{1}_of_{2}", taskName, new Integer(nodeCount), new Integer(4500))); //$NON-NLS-1$ // Convert any schema element ref or substitution group objects final XSDComponent ref = resolveSchemaRef(xsdComponent); // check for recursion and isExisting constants boolean checkRecursion = false; boolean isRecursive = false; boolean checkExisting = false; if (ref != null) { if (recursionStack.contains(ref)) isRecursive = true; recursionStack.push(ref); } else { if (recursionStack.contains(xsdComponent)) isRecursive = true; recursionStack.push(xsdComponent); } // Capture existing children info XmlDocumentEntity documentChild = documentElement; updateDeleteMap(documentElement, null); final XmlDocumentEntity existingChild = findExistingDocumentChild((ArrayList)updateDeleteMap.get(documentElement), xsdComponent); if (isAllCompositor(xsdComponent)) { XmlAll temp = null; // Attempt to find an existing all (this occurs when the complex type that uses // an all extends another complex type that has an all); final ArrayList currentChildren = (ArrayList)updateCurrentChildMap.get(documentElement); if (currentChildren != null) { final Iterator iter = currentChildren.iterator(); while (iter.hasNext()) { final Object sibling = iter.next(); if (sibling instanceof XmlAll) { temp = (XmlAll)sibling; // use this all component .. documentChild = temp; break; } } } // If we didn't find one under the documentElement, look for one to reuse if (temp == null) { final ArrayList oldChildren = (ArrayList)updateDeleteMap.get(documentElement); if (oldChildren != null) { final Iterator childrenIT = oldChildren.iterator(); while (childrenIT.hasNext()) { final Object sibling = childrenIT.next(); if (sibling instanceof XmlAll) { temp = (XmlAll)sibling; // use this all component .. documentChild = temp; break; } } } } if (temp != null) updateDeleteMap(documentElement, documentChild); if (ref != null) xsdComponent = ref; updateMonitor(); checkRecursion = true; } else if (isSequenceCompositor(xsdComponent)) { XmlSequence temp = null; // Attempt to find an existing sequence (this occurs when the complex type that uses // a sequence extends another complex type that has a sequence); final ArrayList currentChildren = (ArrayList)updateCurrentChildMap.get(documentElement); if (currentChildren != null) { final Iterator iter = currentChildren.iterator(); while (iter.hasNext()) { final EObject sibling = (EObject)iter.next(); if (sibling instanceof XmlSequence) // check the min / max occurs the the schema references // if they match then use this sequence if (isValidSequenceForReuse(findSchemaReference(sibling), xsdComponent)) { temp = (XmlSequence)sibling; // use this sequence .. documentChild = temp; break; } } } // If we didn't find one under the documentElement, look for one to reuse if (temp == null) { final ArrayList oldChildren = (ArrayList)updateDeleteMap.get(documentElement); if (oldChildren != null) { final Iterator childrenIT = oldChildren.iterator(); while (childrenIT.hasNext()) { final EObject sibling = (EObject)childrenIT.next(); if (sibling instanceof XmlSequence) // check the min / max occurs the the schema references // if they match then use this sequence if (isValidSequenceForReuse(findSchemaReference(sibling), xsdComponent)) { temp = (XmlSequence)sibling; // use this sequence .. documentChild = temp; break; } } } } if (temp != null) updateDeleteMap(documentElement, documentChild); updateMonitor(); } else if (existingChild != null) { documentChild = existingChild; updateDeleteMap(documentElement, documentChild); nodeCount++; updateMonitor(); checkExisting = true; checkRecursion = (xsdComponent instanceof XSDElementDeclaration) && !((XSDElementDeclaration)xsdComponent).isAbstract(); } else checkRecursion = (xsdComponent instanceof XSDElementDeclaration) && !((XSDElementDeclaration)xsdComponent).isAbstract(); if (isRecursive && checkRecursion) { final ObjectID uuid = ModelerCore.getObjectId(documentChild); if (recursiveElementUUIDs.contains(uuid)) { if (!recursionStack.isEmpty()) recursionStack.pop(); return; } recursiveElementUUIDs.add(uuid); if (!recursionStack.isEmpty()) recursionStack.pop(); return; } // The element and attributes in the schema should have a type that is either // a SimpleType or ComplexType; use that and build child objects according to the type // However, DO NOT do this if the type is an anonymous type below the schema element, // since children are handled below! final XSDComponent typeEntity = XmlDocumentUtil.findXSDType(xsdComponent); if (typeEntity != null && typeEntity.eContainer() != null && !typeEntity.eContainer().equals(xsdComponent)) if (!checkExisting || (checkExisting && existingChild != null)) updateDelete(documentChild, typeEntity, taskName); // Now add this Node's Children (like attributes) to the new Node (or this node if the schema element was not valid) final Iterator children = xsdComponent.eContents().iterator(); while (children.hasNext()) { final EObject schemaChild = (EObject)children.next(); // may be XSDDiagnostic if validated! if (!checkExisting || (checkExisting && existingChild != null)) if (schemaChild instanceof XSDComponent) updateDelete(documentChild, (XSDComponent)schemaChild, taskName); } if (!recursionStack.isEmpty()) recursionStack.pop(); } /** * Updates the updateDeleteMap for given document entity The entities in the value collection have been added to the * documentEntity as children via the updateChildren method. When the update is complete any children of the document entity * which are not in the collection in the updateReUseMap need to be deleted from the model. The updateCurrentChildMap should be * updated as well */ private void updateDeleteMap( final XmlDocumentEntity documentEntity, final XmlDocumentEntity child ) { // If child == null initialize the entry if (child == null) { if (updateDeleteMap.get(documentEntity) == null) { final ArrayList children = new ArrayList(documentEntity.eContents()); updateDeleteMap.put(documentEntity, children); } return; } // Update the updateDeleteMap ArrayList children = (ArrayList)updateDeleteMap.get(documentEntity); if (children != null) { children.remove(child); updateDeleteMap.put(documentEntity, children); } // Update the updateCurrentChildMap children = (ArrayList)updateCurrentChildMap.get(documentEntity); if (children == null) children = new ArrayList(); children.add(child); updateCurrentChildMap.put(documentEntity, children); } /** * Update the XML Document from the given Root MetaObject down. This will remove xml doc nodes that do not exist in the schema, * add xml doc nodes for schema nodes that are missing and ensure indexes are the same. * * @param root - current root element * @param monitor * @param boolean true if performing the adds. */ private void updateFromSchema( final XmlElement rootElement, final boolean doAdds ) throws ModelerCoreException { this.root = rootElement; newCount = 0; // Resolve the Schema definition for the root object final XSDComponent schemaComponent = root.getXsdComponent(); // The root Element in the schema should have a type that is either // a SimpleType or ComplexType final XSDTypeDefinition typeDefn = XmlDocumentUtil.findXSDType(schemaComponent); if (typeDefn != null) { // Set the units of work on the monitor nodeCount = 1; monitor.beginTask(CALCULATING_MSG2 + root, 5000); initializeRecursionStack(root); // countNodes(typeDefn, CALCULATING_MSG2 + root, false); monitor.worked(500); monitor.subTask(UPDATING_MSG + root); // Calculate factor to use when adding work for each remaining node. final double temp = 4500d / nodeCount; if (temp > 0) this.factor = 4500 / nodeCount; else this.factor = new Double(0 - (1 / (4500d / 9200))).intValue(); initializeRecursionStack(root); nodeCount = 0; if (doAdds) updateAdd(root, typeDefn, UPDATING_MSG + root); else updateDelete(root, typeDefn, UPDATING_MSG + root); } } /* (non-Javadoc) * @see org.teiid.designer.metamodels.xml.XmlDocumentBuilder#updateFromSchema(org.teiid.designer.metamodels.xml.XmlElement, org.eclipse.core.runtime.IProgressMonitor) */ @Override public int updateFromSchema( final XmlElement rootElement, final IProgressMonitor progressMonitor ) throws ModelerCoreException { CoreArgCheck.isNotNull(rootElement); this.root = rootElement; int count = 0; this.monitor = progressMonitor != null ? progressMonitor : new NullProgressMonitor(); // Start UoW if neccessary final boolean startedTxn = ModelerCore.startTxn(true, true, XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Update_XML_Document_from_Schema_6"), this); //$NON-NLS-1$ boolean succeeded = false; try { // First do all the deletes - Do deletes first to ensure indexes are correct updateFromSchema(root, false); // Process all the deletes. - Ensure model has no extra nodes prior to beginning adds count = performTreeUpdates(); // reset the attributes for tracking namespaces referencedSchemas.clear(); initializeSchemaUris(rootElement); // Now do the Adds updateFromSchema(root, true); count = count + newCount + moveCount; succeeded = true; } finally { cleanup(); // Commit UoW if we started it. if (startedTxn) { if (succeeded) ModelerCore.commitTxn(); else ModelerCore.rollbackTxn(); } } return count; } /** * Update the monitor for a completed entity using factor computed during countNodes */ private void updateMonitor() { if (factor > 0) { monitor.worked(factor); return; } tempFactor--; if (tempFactor == factor) { monitor.worked(1); tempFactor = 0; } } private void updateSchemaUris( final EObject docNode, final XSDComponent xsdComp ) { if (xsdComp == null || docNode == null) return; final XSDSchema schema = xsdComp.getSchema(); if (schema == null) { // Then this is likely an XSD component that is an element/attribute reference to something // that cannot be resolved. String msg = null; if (xsdComp instanceof XSDNamedComponent) { final XSDNamedComponent namedComp = (XSDNamedComponent)xsdComp; final Object[] params = new Object[] {namedComp.getName()}; msg = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Unable_to_resolve_XSD_reference_to", params); //$NON-NLS-1$ } else { final IPath path = ModelerCore.getModelEditor().getModelRelativePath(docNode); final Object[] params = new Object[] {path}; msg = XmlDocumentPlugin.Util.getString("XmlDocumentBuilderImpl.Unable_to_resolve_XSD_reference_for", params); //$NON-NLS-1$ } XmlDocumentPlugin.Util.log(IStatus.ERROR, msg); return; } // check to see if we've already done this schema if (referencedSchemas.contains(schema)) return; referencedSchemas.add(schema); // find the root element XmlElement root = null; EObject node = docNode; while (root == null && node != null) if (node instanceof XmlRoot) root = (XmlElement)node; else node = node.eContainer(); final Map qNameMap = schema.getQNamePrefixToNamespaceMap(); if (qNameMap != null) { final Iterator entries = qNameMap.entrySet().iterator(); while (entries.hasNext()) { final Map.Entry next = (Map.Entry)entries.next(); final String prefix = (String)next.getKey(); final String uri = (String)next.getValue(); final XmlNamespace ns = docFactory.createXmlNamespace(); if ("this".equals(prefix) || prefix == null || prefix.trim().length() == 0) ns.setPrefix(XmlDocumentUtil.createXmlPrefixFromUri(uri)); //$NON-NLS-1$ else ns.setPrefix(prefix); ns.setUri(uri); ns.setElement(root); if (!schemaUris.contains(uri)) { schemaUris.add(uri); this.uriToNamespaceMap.put(uri, ns); } } } } }