/******************************************************************************* * Copyright (c) 2010 SAP AG. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Emil Simeonov - initial API and implementation. * Dimitar Donchev - initial API and implementation. * Dimitar Tenev - initial API and implementation. * Nevena Manova - initial API and implementation. * Georgi Konstantinov - initial API and implementation. *******************************************************************************/ package org.eclipse.wst.sse.sieditor.model.utils; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.wst.wsdl.Definition; import org.eclipse.xsd.XSDConcreteComponent; import org.eclipse.xsd.XSDNamedComponent; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.util.XSDConstants; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.eclipse.wst.sse.sieditor.model.api.IModelRoot; import org.eclipse.wst.sse.sieditor.model.api.IWsdlModelRoot; import org.eclipse.wst.sse.sieditor.model.api.IXSDModelRoot; import org.eclipse.wst.sse.sieditor.model.xsd.api.ISchema; public class EmfModelPatcher implements IEmfModelPatcher { private static final int MAX_PARENTS_TO_REFRESH = 10; private static final int MAX_SEARCH_DEPTH = 15; private static final IEmfModelPatcher INSTANCE = new EmfModelPatcher(); private EmfModelPatcher() { } public static IEmfModelPatcher instance() { return INSTANCE; } @Override public void patchEMFModelAfterDomChange(final IModelRoot modelRoot, final Set<Node> changedNodes) { if (modelRoot instanceof IXSDModelRoot) { this.patchXsdModelAfterDomChange((IXSDModelRoot) modelRoot, changedNodes); } else { this.patchWsdlModelAfterDomChange((IWsdlModelRoot) modelRoot, changedNodes); } changedNodes.clear(); } private void patchWsdlModelAfterDomChange(final IWsdlModelRoot wsdlRoot, final Set<Node> changedNodes) { final Definition definition = wsdlRoot.getDescription().getComponent(); final Element element = definition.getElement(); if (element == null) { return; } definition.elementChanged(element); refreshDefinitionTargetNamespace(definition, element); if (EmfWsdlUtils.isSchemaForSchemaMissingForAnySchema(wsdlRoot)) { return; } final List<ISchema> containedSchemas = wsdlRoot.getDescription().getContainedSchemas(); checkTargetNamespaceAttribute(containedSchemas); updateChangedNodes(changedNodes, definition, containedSchemas); for (final ISchema schema : containedSchemas) { this.patchXsdModelAfterDomChange((IXSDModelRoot) schema.getModelRoot(), (HashSet<Node>) changedNodes); } } /** * Explicit refresh of the targetNamespace in case of missing such * attribute. Due to Bug in EMF model - TNS is not updated if the attribute * is missing, though the change might be the sole attribute's deletion. */ private void refreshDefinitionTargetNamespace(final Definition definition, final Element element) { if (!element.hasAttribute(XSDConstants.TARGETNAMESPACE_ATTRIBUTE)) { definition.setTargetNamespace(null); } } private void patchXsdModelAfterDomChange(final IXSDModelRoot iXsdModelRoot, final Set<Node> changedNodes) { final ISchema iSchema = iXsdModelRoot.getSchema(); final XSDSchema xsdSchema = iSchema.getComponent(); if (xsdSchema.getElement() == null) { return; } xsdSchema.elementChanged(xsdSchema.getElement()); if (EmfXsdUtils.isSchemaForSchemaMissing(iSchema)) { return; } this.updateChangedNodes(changedNodes, iSchema); } private void updateChangedNodes(final Set<Node> changedNodes, final Definition definition, final List<ISchema> containedSchemas) { final Set<Node> updatedNodes = updateChangedNodes(changedNodes, containedSchemas); changedNodes.removeAll(updatedNodes); } private void updateChangedNodes(final Set<Node> changedNodes, final ISchema schema) { final List<ISchema> schemas = new LinkedList<ISchema>(); schemas.add(schema); final Set<Node> updatedNodes = updateChangedNodes(changedNodes, schemas); changedNodes.removeAll(updatedNodes); } private Set<Node> updateChangedNodes(final Set<Node> changedNodes, final List<ISchema> containedSchemas) { final Set<XSDConcreteComponent> updatedComponents = new HashSet<XSDConcreteComponent>(); final Set<Node> updatedNodes = new HashSet<Node>(); for (final Node node : changedNodes) { for (final ISchema containedSchema : containedSchemas) { final XSDSchema xsdSchema = containedSchema.getComponent(); final XSDConcreteComponent component = findCorrespondingRootComponent(node, xsdSchema); if (component != null && !updatedComponents.contains(component) && xsdSchema != component) { updatedComponents.add(component); updatedNodes.add(node); EmfXsdUtils.updateModelReferencers(xsdSchema, component); break; } } } return updatedNodes; } private XSDConcreteComponent findCorrespondingRootComponent(final Node initialNode, final XSDSchema containerSchema) { XSDConcreteComponent component = null; Node currentNode = initialNode; int currentDepth = 0; do { if (currentNode instanceof Attr) { currentNode = ((Attr) currentNode).getOwnerElement(); } if (currentNode == null) { break; } if (component != null && component.eContainer() instanceof XSDConcreteComponent) { component = (XSDConcreteComponent) component.eContainer(); } else { component = containerSchema.getCorrespondingComponent(currentNode); } refreshComponents(component); if (component instanceof XSDNamedComponent && ((XSDNamedComponent) component).getName() == null) { component = containerSchema; } currentNode = currentNode.getParentNode(); currentDepth++; } while (component instanceof XSDSchema && currentNode != null && !currentNode.getNodeName().endsWith(XSDConstants.SCHEMA_ELEMENT_TAG) && currentDepth < MAX_SEARCH_DEPTH); return component; } // ========================================================= // helpers // ========================================================= /** * check the state of targetNamespace attribute and explicit refresh of the * targetNamespace in case of missing such attribute due to the bug: <br> * TNS is not updated if the attribute is missing, though the change might * be the sole attribute's deletion * */ private void checkTargetNamespaceAttribute(final List<ISchema> containedSchemas) { for (final ISchema schema : containedSchemas) { if (schema == null) { continue; } final XSDSchema currerntSchema = schema.getComponent(); final Element element = currerntSchema.getElement(); if (element == null) { continue; } if (!element.hasAttribute(XSDConstants.TARGETNAMESPACE_ATTRIBUTE)) { currerntSchema.setTargetNamespace(null); } } } private void refreshComponents(final XSDConcreteComponent component) { refreshComponentsInternal(component, 0); } private void refreshComponentsInternal(final XSDConcreteComponent component, final int currentRefreshLevel) { if (component == null) { return; } component.elementChanged(component.getElement()); // if (component instanceof XSDNamedComponent && ((XSDNamedComponent) // component).getName() != null) { // return; // } if (currentRefreshLevel < MAX_PARENTS_TO_REFRESH && component.eContainer() instanceof XSDConcreteComponent) { refreshComponentsInternal((XSDConcreteComponent) component.eContainer(), currentRefreshLevel + 1); } } }