/*******************************************************************************
* 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.reconcile.utils;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.wst.sse.sieditor.command.emf.common.utils.UpdateNSPrefixUtils;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.searchresultprocessor.IXsdSearchResultProcessor;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.searchresultprocessor.XsdAttributeDeclarationSearchResultProcessor;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.searchresultprocessor.XsdElementDeclarationSearchResultProcessor;
import org.eclipse.wst.sse.sieditor.model.utils.ElementAttributeUtils;
import org.eclipse.wst.sse.sieditor.model.utils.EmfXsdUtils;
import org.eclipse.wst.wsdl.Message;
import org.eclipse.wst.wsdl.Operation;
import org.eclipse.wst.wsdl.Part;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDImport;
import org.eclipse.xsd.XSDInclude;
import org.eclipse.xsd.XSDNamedComponent;
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;
public class ObjectsForResolveUtils {
private static final ObjectsForResolveUtils INSTANCE = new ObjectsForResolveUtils();
private ObjectsForResolveUtils() {
}
public static ObjectsForResolveUtils instance() {
return INSTANCE;
}
// =========================================================
// unresolved elements search helper
// =========================================================
public ObjectsForResolveContainer findObjectsForResolve(final EObject eObject, final List<XSDSchema> allSchemas) {
final List<EObject> visited = new LinkedList<EObject>();
return findObjectsForResolveInternal(eObject, allSchemas, visited);
}
/**
* Utility internal method. Returns container with all the elements, which
* need reconciling.
*/
private ObjectsForResolveContainer findObjectsForResolveInternal(final EObject eObject,
final List<XSDSchema> allSchemas, final List<EObject> visited) {
final ObjectsForResolveContainer container = new ObjectsForResolveContainer();
final Iterator<EObject> eAllContents = eObject.eContents().iterator();
while (eAllContents.hasNext()) {
final EObject eContent = eAllContents.next();
if (visited.contains(eContent)) {
continue;
}
processXsdElementDeclaration(container, allSchemas, eContent);
processXsdAttributeDeclaration(container, allSchemas, eContent);
processComplexTypeDefinition(container, allSchemas, eContent);
processWsdlOperation(container, eContent);
processWsdlMessage(container, eContent);
processWsdlMessagePart(container, allSchemas, eContent);
// TODO: needs to resolve XsdAttributeGroupDefinition &&
// XsdModelGroupDefinition
visited.add(eContent);
final ObjectsForResolveContainer childsContainer = findObjectsForResolveInternal(eContent, allSchemas,
visited);
container.addAll(childsContainer);
}
return container;
}
// =========================================================
// findObjectsForResolve helpers
// =========================================================
private void processXsdElementDeclaration(final ObjectsForResolveContainer container,
final List<XSDSchema> allSchemas, final EObject eContent) {
if (!(eContent instanceof XSDElementDeclaration)) {
return;
}
processXsdNamedDeclaration(allSchemas, (XSDElementDeclaration) eContent,
XsdElementDeclarationSearchResultProcessor.instance(), container);
}
private void processXsdAttributeDeclaration(final ObjectsForResolveContainer container,
final List<XSDSchema> allSchemas, final EObject eContent) {
if (!(eContent instanceof XSDAttributeDeclaration)) {
return;
}
processXsdNamedDeclaration(allSchemas, (XSDAttributeDeclaration) eContent,
XsdAttributeDeclarationSearchResultProcessor.instance(), container);
}
private <T extends XSDNamedComponent> void processXsdNamedDeclaration(final List<XSDSchema> allSchemas,
final T xsdNamedDeclaration, final IXsdSearchResultProcessor<T> xsdSearchResultProcessor,
final ObjectsForResolveContainer container) {
if (xsdSearchResultProcessor.isReference(xsdNamedDeclaration)) {
final T resolvedDeclaration = xsdSearchResultProcessor.getReference(xsdNamedDeclaration);
final boolean prefixAndNamespaceValid = areDestinationSchemaPrefixAndNamespaceValid(
xsdNamedDeclaration.getSchema(), xsdNamedDeclaration.getElement(), XSDConstants.REF_ATTRIBUTE,
resolvedDeclaration.getTargetNamespace());
if (!prefixAndNamespaceValid) {
xsdSearchResultProcessor.getResolveCollection(xsdNamedDeclaration, container).add(xsdNamedDeclaration);
}
if (resolvedDeclaration.eContainer() == null
|| !isContainerXsdSchemaValid(allSchemas, xsdNamedDeclaration, resolvedDeclaration.getSchema(),
resolvedDeclaration.getTargetNamespace())) {
xsdSearchResultProcessor.getResolveCollection(xsdNamedDeclaration, container).add(xsdNamedDeclaration);
}
} else {
final XSDTypeDefinition typeDefinition = xsdSearchResultProcessor.getTypeDefinition(xsdNamedDeclaration);
final boolean prefixAndNamespaceValid = areDestinationSchemaPrefixAndNamespaceValid(
xsdNamedDeclaration.getSchema(), xsdNamedDeclaration.getElement(), XSDConstants.TYPE_ATTRIBUTE,
typeDefinition != null ? typeDefinition.getTargetNamespace() : null);
if (!prefixAndNamespaceValid) {
xsdSearchResultProcessor.getResolveCollection(xsdNamedDeclaration, container).add(xsdNamedDeclaration);
}
if (typeDefinition != null
&& typeDefinition.eContainer() == null
|| !isContainerXsdSchemaValid(allSchemas, xsdNamedDeclaration, typeDefinition.getSchema(),
typeDefinition.getTargetNamespace())) {
if (!EmfXsdUtils.isSchemaForSchemaNS(typeDefinition.getTargetNamespace())) {
xsdSearchResultProcessor.getResolveCollection(xsdNamedDeclaration, container).add(
xsdNamedDeclaration);
}
}
}
}
private void processComplexTypeDefinition(final ObjectsForResolveContainer container,
final List<XSDSchema> allSchemas, final EObject eContent) {
if (!(eContent instanceof XSDComplexTypeDefinition)) {
return;
}
final XSDComplexTypeDefinition complexType = (XSDComplexTypeDefinition) eContent;
final XSDTypeDefinition baseTypeDefinition = complexType.getBaseTypeDefinition();
// destinationSchemaPrefixAndNamespaceAreValid(complexType.getSchema(),
// complexType.getElement(),
// XSDConstants.BASE_ATTRIBUTE);
if (baseTypeDefinition != null
&& baseTypeDefinition.eContainer() == null
|| !isContainerXsdSchemaValid(allSchemas, complexType, baseTypeDefinition.getSchema(),
baseTypeDefinition.getTargetNamespace())) {
container.getComplexTypesForExtensionResolve().add(complexType);
container.getComplexTypesForRestrictionResolve().add(complexType);
}
}
private void processWsdlOperation(final ObjectsForResolveContainer container, final EObject eContent) {
if (!(eContent instanceof Operation)) {
return;
}
final Operation operation = (Operation) eContent;
container.getOperationsForResolve().add(operation);
}
private void processWsdlMessage(final ObjectsForResolveContainer container, final EObject eContent) {
if (!(eContent instanceof Message)) {
return;
}
final Message message = (Message) eContent;
container.getMessagesForResolve().add(message);
}
private void processWsdlMessagePart(final ObjectsForResolveContainer container, final List<XSDSchema> allSchemas,
final EObject eContent) {
if (!(eContent instanceof Part)) {
return;
}
final Part part = (Part) eContent;
if ((part.getElementDeclaration() != null && (part.getElementDeclaration().eContainer() == null || !allSchemas
.contains(part.getElementDeclaration().getSchema())))
|| (part.getTypeDefinition() != null && (part.getTypeDefinition().eContainer() == null || !allSchemas
.contains(part.getTypeDefinition().getSchema())))) {
container.getMessagePartsForResolve().add(part);
}
}
// =========================================================
// helpers
// =========================================================
public boolean isContainerXsdSchemaValid(final List<XSDSchema> allSchemas, final XSDNamedComponent sourceComponent,
final XSDSchema destinationSchema, final String destinationSchemaTargetNamespace) {
if (destinationSchema == null) {
return false;
}
final String destinationSchemaLocation = destinationSchema.getSchemaLocation();
final String sourceSchemaLocation = sourceComponent.getSchema().getSchemaLocation();
final boolean resolveComponentSchemaIsInSameDocument = destinationSchemaLocation != null
&& destinationSchemaLocation.equals(sourceSchemaLocation);
final String sourceSchemaTNS = sourceComponent.getSchema().getTargetNamespace();
final boolean sourceAndDestinationNamespaceAreTheSame = (sourceSchemaTNS == null && destinationSchemaTargetNamespace == null)
|| (sourceSchemaTNS != null && sourceSchemaTNS.equals(destinationSchemaTargetNamespace));
final boolean needsImportOrIncludeDirective = !(resolveComponentSchemaIsInSameDocument && sourceAndDestinationNamespaceAreTheSame);
boolean hasImportOrIncludeDirective = true;
if (needsImportOrIncludeDirective) {
hasImportOrIncludeDirective = hasImportOrIncludeDirective(sourceComponent.getSchema(), destinationSchema,
destinationSchemaTargetNamespace);
}
final boolean isReachable = !needsImportOrIncludeDirective || needsImportOrIncludeDirective
&& hasImportOrIncludeDirective;
return isReachable
&& (!resolveComponentSchemaIsInSameDocument || resolveComponentSchemaIsInSameDocument
&& isDestinationSchemaInAllSchemas(allSchemas, destinationSchema));
}
private boolean isDestinationSchemaInAllSchemas(final List<XSDSchema> allSchemas, final XSDSchema destinationSchema) {
for (final XSDSchema xsdSchema : allSchemas) {
if (xsdSchema.getTargetNamespace() == null && destinationSchema.getTargetNamespace() == null) {
return true;
}
if (xsdSchema.getTargetNamespace() != null
&& xsdSchema.getTargetNamespace().equals(destinationSchema.getTargetNamespace())) {
return true;
}
}
return false;
}
/**
* Checks if the given source schema has access to the destinationSchema. If
* destinationSchemaTargetNamespace is for schema for schema always returns
* true.
*/
private boolean hasImportOrIncludeDirective(final XSDSchema sourceSchema, final XSDSchema destinationSchema,
final String destinationSchemaTargetNamespace) {
if (EmfXsdUtils.isSchemaForSchemaNS(destinationSchemaTargetNamespace)) {
return true;
}
if (destinationSchemaTargetNamespace == null) {
return false;
}
final EList<XSDSchemaContent> sourceComponentSchemaContents = sourceSchema.getContents();
for (final XSDSchemaContent content : sourceComponentSchemaContents) {
if (content instanceof XSDImport) {
final XSDImport xsdImport = (XSDImport) content;
if (destinationSchemaTargetNamespace.equals(xsdImport.getNamespace())) {
return true;
}
} else if (content instanceof XSDInclude) {
final XSDInclude include = (XSDInclude) content;
final String includeSchemaLocation = include.getSchema().getSchemaLocation();
final String destinationSchemaLocation = destinationSchema.getSchema().getSchemaLocation();
if (includeSchemaLocation != null && includeSchemaLocation.equals(destinationSchemaLocation)) {
return true;
}
}
}
return false;
}
// =========================================================
// helpers
// =========================================================
/**
* This method checks if the prefix value of the given element is as
* expected.
*/
private boolean areDestinationSchemaPrefixAndNamespaceValid(final XSDSchema xsdSchema, final Element element,
final String attributeName, final String expectedDestinationSchemaNamespace) {
// FIXME: Intentionally disabling the prefix checks. The WSDL API is not
// running the validation on the XSDs when validation performed on the
// Definition. The error markers cannot be "removed" when undo of
// operation which breaks the prefix value.
if (true) {
return true;
}
if (expectedDestinationSchemaNamespace == null) {
return true;
}
final String destinationNamespacePrefix = getDestinationNamespacePrefix(element, attributeName);
if (destinationNamespacePrefix == null) {
// no prefix, same schema
return true;
}
final String actualDestinationNamespace = xsdSchema.getQNamePrefixToNamespaceMap().get(
destinationNamespacePrefix);
return expectedDestinationSchemaNamespace.equals(actualDestinationNamespace);
}
private String getDestinationNamespacePrefix(final Element element, final String attributeName) {
if (element == null) {
return null;
}
if (!ElementAttributeUtils.hasAttributeValue(element, attributeName)) {
return null;
}
final String qName = element.getAttribute(attributeName);
return UpdateNSPrefixUtils.instance().extractPrefixFromQName(qName);
}
}