/******************************************************************************* * 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. * Stanislav Nichev - initial API and implementation. *******************************************************************************/ package org.eclipse.wst.sse.sieditor.command.emf.common.utils; import java.util.Collection; import java.util.LinkedList; import java.util.List; import javax.xml.namespace.QName; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.query.conditions.eobjects.EObjectCondition; import org.eclipse.emf.query.conditions.eobjects.structuralfeatures.EObjectReferencerCondition; import org.eclipse.emf.query.statements.FROM; import org.eclipse.emf.query.statements.IQueryResult; import org.eclipse.emf.query.statements.SELECT; import org.eclipse.emf.query.statements.WHERE; import org.eclipse.wst.wsdl.Binding; import org.eclipse.wst.wsdl.Definition; import org.eclipse.wst.wsdl.Message; import org.eclipse.wst.wsdl.Part; import org.eclipse.wst.wsdl.Port; import org.eclipse.wst.wsdl.PortType; import org.eclipse.wst.wsdl.Service; import org.eclipse.wst.wsdl.util.WSDLConstants; import org.eclipse.xsd.XSDAttributeDeclaration; import org.eclipse.xsd.XSDAttributeGroupDefinition; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDModelGroupDefinition; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDSchemaContent; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.util.XSDConstants; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.eclipse.wst.sse.sieditor.model.reconcile.utils.WsdlReconcileUtils; /** * This is the element references updater class. This class has methods for * remapping definition message parts and schema element references. * */ public class RemapReferencesUtils { private static final RemapReferencesUtils INSTANCE = new RemapReferencesUtils(); private RemapReferencesUtils() { } public static RemapReferencesUtils instance() { return INSTANCE; } // ========================================================= // element schema references remap methods // ========================================================= /** * Method for remapping schema references * * @param baseComponent * @param changedSchema * @param namespacePrefix */ public void remapSchemaReferences(final EObject baseComponent, final XSDSchema changedSchema, final String namespacePrefix) { final EList<XSDSchemaContent> contents = changedSchema.getContents(); for (final XSDSchemaContent xsdSchemaContent : contents) { if (needsToRemapReferencesForContent(xsdSchemaContent)) { final EObjectCondition refCondition = new EObjectReferencerCondition(xsdSchemaContent); final IQueryResult result = new SELECT(new FROM(baseComponent), new WHERE(refCondition)).execute(); processQueryResult(changedSchema, namespacePrefix, xsdSchemaContent, result); } } } /** * Utility method. Checks if we need to start the remapping of references * for the given schema content. * * @param xsdSchemaContent * @return */ private boolean needsToRemapReferencesForContent(final XSDSchemaContent xsdSchemaContent) { return xsdSchemaContent instanceof XSDElementDeclaration || xsdSchemaContent instanceof XSDTypeDefinition || xsdSchemaContent instanceof XSDAttributeDeclaration || xsdSchemaContent instanceof XSDAttributeGroupDefinition || xsdSchemaContent instanceof XSDModelGroupDefinition; } private void processQueryResult(final XSDSchema changedSchema, final String namespacePrefix, final XSDSchemaContent xsdSchemaContent, final IQueryResult result) { for (final Object next : result) { if (next instanceof XSDSchema || next instanceof Definition) { continue; // we do not need to check root elements } if (next != xsdSchemaContent) { if (next instanceof XSDComplexTypeDefinition) { processComplexTypeDefinition(changedSchema, namespacePrefix, next, xsdSchemaContent); } else if (next instanceof XSDParticle) { processParticle(changedSchema, namespacePrefix, next); } else if (next instanceof XSDElementDeclaration) { processElementDeclaration(changedSchema, namespacePrefix, next); } else if (next instanceof XSDAttributeDeclaration) { processAttributeDeclaration(changedSchema, namespacePrefix, next); } else if (next instanceof XSDAttributeGroupDefinition) { processAttributeGroupDefinition(changedSchema, namespacePrefix, next); } else if (next instanceof XSDModelGroupDefinition) { processModelGroupDefinition(changedSchema, namespacePrefix, next); } } } } private void processComplexTypeDefinition(final XSDSchema changedSchema, final String namespacePrefix, final Object next, final XSDSchemaContent xsdSchemaContent) { if (xsdSchemaContent instanceof XSDTypeDefinition) { final XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition) next; final Element complexTypeContentElement = getComplexTypeContentElement(complexType.getElement()); if (complexTypeContentElement == null) { return; } processContentElement(changedSchema, namespacePrefix, xsdSchemaContent, complexType, complexTypeContentElement, XSDConstants.EXTENSION_ELEMENT_TAG); processContentElement(changedSchema, namespacePrefix, xsdSchemaContent, complexType, complexTypeContentElement, XSDConstants.RESTRICTION_ELEMENT_TAG); } } private void processContentElement(final XSDSchema changedSchema, final String namespacePrefix, final XSDSchemaContent xsdSchemaContent, final XSDComplexTypeDefinition complexType, final Element complexTypeContentElement, final String derivationMethod) { final Element contentElement = getComplexTypeContainingElement(complexTypeContentElement, derivationMethod); elementPrefixUtils().updateSchemaContent(changedSchema, namespacePrefix, contentElement, complexType, true, xsdSchemaContent.getSchema().getTargetNamespace(), xsdSchemaContent.getSchema()); } // ========================================================= // complex type content helpers // ========================================================= /** * Helper method. Returns the complexContent or simpleContent DOM element * for the given complex type. Returns <code>null</code> if no element was * found. */ public Element getComplexTypeContentElement(final Element element) { final LinkedList<String> elementTags = new LinkedList<String>(); elementTags.add(XSDConstants.COMPLEXCONTENT_ELEMENT_TAG); elementTags.add(XSDConstants.SIMPLECONTENT_ELEMENT_TAG); return getComplexTypeContentElement(element, elementTags); } public Element getComplexTypeContainingElement(final Element containerElement, final String derivationMethodTag) { final LinkedList<String> elementTags = new LinkedList<String>(); elementTags.add(derivationMethodTag); return getComplexTypeContentElement(containerElement, elementTags); } private Element getComplexTypeContentElement(final Element complexTypeContentElement, final Collection<String> elementTags) { final NodeList contentChildNodes = complexTypeContentElement.getChildNodes(); for (int i = 0; i < contentChildNodes.getLength(); i++) { final Node childNode = contentChildNodes.item(i); final String nodeFullName = childNode.getNodeName(); final String nodeSimpleName = nodeFullName.substring(nodeFullName.indexOf(':') + 1); if (childNode instanceof Element && elementTags.contains(nodeSimpleName)) { return (Element) childNode; } } return null; } // ========================================================= // end of complex type content helpers // ========================================================= private void processParticle(final XSDSchema changedSchema, final String namespacePrefix, final Object next) { final XSDParticle particle = (XSDParticle) next; if (particle.getContent() instanceof XSDElementDeclaration) { final XSDElementDeclaration xsdElementDeclaration = (XSDElementDeclaration) particle.getContent(); elementPrefixUtils().updateXsdElementDeclaration(changedSchema, namespacePrefix, xsdElementDeclaration); } } private void processElementDeclaration(final XSDSchema changedSchema, final String namespacePrefix, final Object next) { final XSDElementDeclaration xsdElementDeclaration = (XSDElementDeclaration) next; elementPrefixUtils().updateXsdElementDeclaration(changedSchema, namespacePrefix, xsdElementDeclaration); } private void processAttributeDeclaration(final XSDSchema changedSchema, final String namespacePrefix, final Object next) { final XSDAttributeDeclaration xsdAttributeDeclaration = (XSDAttributeDeclaration) next; elementPrefixUtils().updateXsdAttributeDeclaration(changedSchema, namespacePrefix, xsdAttributeDeclaration); } private void processAttributeGroupDefinition(final XSDSchema changedSchema, final String namespacePrefix, final Object next) { final XSDAttributeGroupDefinition attributeGroup = (XSDAttributeGroupDefinition) next; elementPrefixUtils().updateXsdAttributeGroupDeclaration(changedSchema, namespacePrefix, attributeGroup); } private void processModelGroupDefinition(final XSDSchema changedSchema, final String namespacePrefix, final Object next) { final XSDModelGroupDefinition xsdModelGroupDefinition = (XSDModelGroupDefinition) next; elementPrefixUtils().updateXsdModelGroupDeclaration(changedSchema, namespacePrefix, xsdModelGroupDefinition); } // ========================================================= // message parts remap methods // ========================================================= /** * Method for remapping the message parts to the new namespace. If the given * schema is <code>null</code>, we are changing definition target namespace */ @SuppressWarnings("unchecked") public void remapMessageParts(final Definition editedDefinition, final XSDSchema schema, final String oldNamespaceValue, final String newNamespaceValue, final String namespacePrefix) { final EList<Message> messages = editedDefinition.getEMessages(); if (messages == null) { return; } for (final Message message : messages) { processMessageParts(editedDefinition, schema, oldNamespaceValue, newNamespaceValue, namespacePrefix, message); } } @SuppressWarnings("unchecked") private void processMessageParts(final Definition editedDefinition, final XSDSchema schema, final String oldNamespaceValue, final String newNamespaceValue, final String namespacePrefix, final Message message) { final List<Part> eParts = message.getEParts(); if (eParts == null) { return; } for (final Part part : eParts) { if (part.getEMessage() != null && part.getEMessage().getEnclosingDefinition() != editedDefinition) { continue; } if (schema == null) { if (part.getElementDeclaration() != null && part.getElementDeclaration().getTargetNamespace().equals(newNamespaceValue)) { // we are changing the targetNamespace of the definition part.setElementName(new QName(newNamespaceValue, part.getElementName().getLocalPart(), namespacePrefix)); } } else if (part.getElementDeclaration() != null && part.getElementDeclaration().getTargetNamespace().equals(oldNamespaceValue)) { if (part.getElementDeclaration().eContainer() == null) { // we need to resolve the element references final XSDElementDeclaration resolvedElementDeclaration = schema.resolveElementDeclaration(newNamespaceValue, part.getElementDeclaration().getName()); part.setElementDeclaration(resolvedElementDeclaration); } if (part.getElementDeclaration().getContainer() == schema) { // update message part QName part.setElementName(new QName(schema.getQNamePrefixToNamespaceMap().get(namespacePrefix), part .getElementName().getLocalPart(), namespacePrefix)); } } } } /** * Utility method. Remaps the definition messages to the new prefix and * namespace.<br> * <br> * <i>NOTE: if we are not processing an imported definition, the * importedDefinition parameter should be the same as the * importingDefinition one.</i> */ @SuppressWarnings("unchecked") public void remapOperationsMessagesReferences(final Definition editedDefinition, final Definition importedDefinition, final String namespaceValue, final String prefix) { final EList<PortType> portTypes = editedDefinition.getEPortTypes(); if (portTypes == null) { return; } for (final PortType portType : portTypes) { WsdlReconcileUtils.instance().reconcileOperationsMessagesReferences(portType.getEOperations(), editedDefinition, importedDefinition.getEMessages(), prefix); } } /** * Utility method. Remaps the difinition bindings to the new prefix and * given namespace.</br> This method ramaps the <code><wsdl:binding * name="NewWSDLFileSOAP2" type="..."></code> "type" references. <br> * <br> * <i>NOTE: if we are not processing an imported definition, the * importedDefinition parameter should be the same as the * importingDefinition one.</i> */ @SuppressWarnings("unchecked") public void remapBindingsPortTypes(final Definition editedDefinition, final Definition importedDefinition, final String namespaceValue) { final EList<Binding> bindings = editedDefinition.getEBindings(); if (bindings == null) { return; } for (final Binding binding : bindings) { remapBindingPortType(importedDefinition, namespaceValue, binding); } } /** * Utility method. Searches the imported definition for suitable port type * to set. */ @SuppressWarnings("unchecked") private void remapBindingPortType(final Definition importedDefinition, final String namespaceValue, final Binding binding) { if (binding.getEPortType() == null) { final String portTypeQName = binding.getElement().getAttribute(WSDLConstants.TYPE_ATTRIBUTE); final String portTypeName = portTypeQName.substring(portTypeQName.indexOf(':') + 1); final EList<PortType> ePortTypes = importedDefinition.getEPortTypes(); for (final PortType portType : ePortTypes) { if (portType.getQName().getLocalPart().equals(portTypeName) && portType.getQName().getNamespaceURI().equals(namespaceValue)) { binding.setEPortType(portType); return; } } } } /** * Method for updating the <wsdl:service><wsdl:port binding="..." /> * port bindings references. */ @SuppressWarnings("unchecked") public void remapServicePortBindings(final Definition editedDefinition, final Definition importedDefinition, final String namespaceValue) { final EList<Service> eServices = editedDefinition.getEServices(); if (eServices != null) { for (final Service service : eServices) { final EList<Port> ePorts = service.getEPorts(); for (final Port port : ePorts) { remapDefinitionPortBinding(importedDefinition, namespaceValue, port); } } } } /** * This method searches for the binding to set based on the <wsdl:port * binding="..." /> attribute value. * */ @SuppressWarnings("unchecked") private void remapDefinitionPortBinding(final Definition importedDefinition, final String namespaceValue, final Port port) { if (port.getEBinding() == null) { // the port binding is not resolved. we need to check all the // bindings from the imported definition to find a matching binding // (we are using the DOMs attribute value for "binding") final String portBindingQName = port.getElement().getAttribute(WSDLConstants.BINDING_ATTRIBUTE); final String bindingName = portBindingQName.substring(portBindingQName.indexOf(':') + 1); final EList<Binding> importedDefinitionBindings = importedDefinition.getEBindings(); for (final Binding importedBinding : importedDefinitionBindings) { if (importedBinding.getQName().getLocalPart().equals(bindingName) && importedBinding.getQName().getNamespaceURI().equals(namespaceValue)) { port.setEBinding(importedBinding); break; } } } } // ========================================================= // helpers // ========================================================= protected UpdateNSPrefixUtils elementPrefixUtils() { return UpdateNSPrefixUtils.instance(); } }