/*******************************************************************************
* 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.model.reconcile.utils;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.wst.wsdl.XSDSchemaExtensibilityElement;
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.impl.XSDImportImpl;
import org.w3c.dom.Element;
import org.eclipse.wst.sse.sieditor.command.emf.common.utils.UpdateNSPrefixUtils;
import org.eclipse.wst.sse.sieditor.model.reconcile.IResolveUtils;
import org.eclipse.wst.sse.sieditor.model.reconcile.ResolveUtils;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.IConcreteComponentResolver;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.XsdAttributeDeclarationAttributeReferenceResolver;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.XsdAttributeDeclarationTypeDefinitionResolver;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.XsdComplexTypeDefinitionExtensionResolver;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.XsdComplexTypeDefinitionRestrictionResolver;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.XsdElementDeclarationElementReferenceResolver;
import org.eclipse.wst.sse.sieditor.model.reconcile.utils.componentresolver.XsdElementDeclarationTypeDefinitionResolver;
/**
* This is the default implementation of the {@link IXsdReconcileUtils}
* interface.
*
*/
public class XsdReconcileUtils implements IXsdReconcileUtils {
/**
* During the resolving of elements, we are checking xsd:imports and
* xsd:includes. This constant tells how "deep" we want to go searching for
* elements for resolve.
*/
private static final int SEARCH_LEVEL_DEPTH = 3;
private static final IXsdReconcileUtils INSTANCE = new XsdReconcileUtils();
private XsdReconcileUtils() {
}
public static IXsdReconcileUtils instance() {
return INSTANCE;
}
// =========================================================
// reconcile schema contents
// =========================================================
@Override
public void reconcileSchemaContents(final XSDSchema schema, final ObjectsForResolveContainer container) {
reconcileSchemaContentsInternal(schema, schema.getQNamePrefixToNamespaceMap(), container, new HashSet<XSDSchema>(), 0,
NamespaceResolverType.ELEMENT);
reconcileSchemaContentsInternal(schema, schema.getQNamePrefixToNamespaceMap(), container, new HashSet<XSDSchema>(), 0,
NamespaceResolverType.SCHEMA);
}
private void reconcileSchemaContentsInternal(final XSDSchema schema, final Map<String, String> prefixesMap,
final ObjectsForResolveContainer container, final Set<XSDSchema> visitedSchemas, final int currentSearchLevel,
final NamespaceResolverType namespaceResolverType) {
if (currentSearchLevel >= SEARCH_LEVEL_DEPTH || visitedSchemas.contains(schema)) {
return;
}
if (currentSearchLevel != 0 && schema.getSchemaLocation() == null && schema.eContainer() == null) {
return;
}
if (!container.getElementsForReferenceResolve().isEmpty()) {
reconcileConcreteComponents(schema, prefixesMap, container.getElementsForReferenceResolve(),
XsdElementDeclarationElementReferenceResolver.instance(), namespaceResolverType);
}
if (!container.getElementsForTypeResolve().isEmpty()) {
reconcileConcreteComponents(schema, prefixesMap, container.getElementsForTypeResolve(),
XsdElementDeclarationTypeDefinitionResolver.instance(), namespaceResolverType);
}
if (!container.getAttributesForReferenceResolve().isEmpty()) {
reconcileConcreteComponents(schema, prefixesMap, container.getAttributesForReferenceResolve(),
XsdAttributeDeclarationAttributeReferenceResolver.instance(), namespaceResolverType);
}
if (!container.getAttributesForTypeResolve().isEmpty()) {
reconcileConcreteComponents(schema, prefixesMap, container.getAttributesForTypeResolve(),
XsdAttributeDeclarationTypeDefinitionResolver.instance(), namespaceResolverType);
}
if (!container.getComplexTypesForExtensionResolve().isEmpty()) {
reconcileConcreteComponents(schema, prefixesMap, container.getComplexTypesForExtensionResolve(),
XsdComplexTypeDefinitionExtensionResolver.instance(), namespaceResolverType);
}
if (!container.getComplexTypesForRestrictionResolve().isEmpty()) {
reconcileConcreteComponents(schema, prefixesMap, container.getComplexTypesForRestrictionResolve(),
XsdComplexTypeDefinitionRestrictionResolver.instance(), namespaceResolverType);
}
visitedSchemas.add(schema);
for (final XSDSchemaContent content : schema.getContents()) {
if (container.areSchemaContentsCollectionsEmpty()) {
return;
}
if (content instanceof XSDImport) {
final XSDImport xsdImport = (XSDImport) content;
XSDSchema resolvedSchema = xsdImport.getResolvedSchema();
if (resolvedSchema == null || resolvedSchema.eContainer() instanceof XSDSchemaExtensibilityElement
&& resolvedSchema.eContainer().eContainer() == null) {
((XSDImportImpl) xsdImport).reset();
resolvedSchema = ((XSDImportImpl) xsdImport).importSchema();
}
if (resolvedSchema != null) {
reconcileSchemaContentsInternal(resolvedSchema, prefixesMap, container, visitedSchemas,
currentSearchLevel + 1, namespaceResolverType);
}
}
if (content instanceof XSDInclude) {
final XSDInclude xsdInclude = (XSDInclude) content;
final XSDSchema resolvedSchema = xsdInclude.getResolvedSchema();
if (resolvedSchema != null) {
reconcileSchemaContentsInternal(resolvedSchema, prefixesMap, container, visitedSchemas,
currentSearchLevel + 1, namespaceResolverType);
}
}
}
}
@SuppressWarnings("unchecked")
private void reconcileConcreteComponents(final XSDSchema xsdSchema, Map<String, String> prefixesMap,
final java.util.List<XSDNamedComponent> componentsForResolve, final IConcreteComponentResolver resolver,
final NamespaceResolverType namespaceResolverType) {
final List<XSDNamedComponent> reconciledConcreteComponents = new LinkedList<XSDNamedComponent>();
for (final XSDNamedComponent sourceComponent : componentsForResolve) {
if (!resolver.shouldProceedWithResolveOf(sourceComponent)) {
continue;
}
final XSDNamedComponent resolvedComponent = resolver.resolveConcreteComponent(sourceComponent, xsdSchema,
namespaceResolverType);
if (resolvedComponent != null) {
final String attributeValue = resolver.getResolveComponentAttributeValue(sourceComponent);
if (attributeValue == null || attributeValue.isEmpty()) {
continue;
}
final String prefix = prefixUtils().extractPrefixFromQName(attributeValue);
String tnsForPrefix = prefixesMap.get(prefix);
if (resolvedComponent.getTargetNamespace() != null
&& !resolvedComponent.getTargetNamespace().equals(tnsForPrefix)) {
// double check prefixes
prefixesMap = sourceComponent.getSchema().getQNamePrefixToNamespaceMap();
tnsForPrefix = prefixesMap.get(prefix);
}
if (isElementCorrectlyResolved(xsdSchema, prefixesMap, xsdSchema.getElement(), resolvedComponent
.getTargetNamespace(), prefix, tnsForPrefix)) {
resolver.componentResolved(sourceComponent, resolvedComponent);
reconciledConcreteComponents.add(sourceComponent);
}
}
}
componentsForResolve.removeAll(reconciledConcreteComponents);
};
// =========================================================
// helpers
// =========================================================
protected IResolveUtils resolveUtils() {
return ResolveUtils.instance();
}
/**
* Utility method. Checks if the prefix is an actual and valid one, so that
* we can safely set the new reference.
*/
private boolean isElementCorrectlyResolved(final XSDSchema schema, final Map<String, String> prefixesMap,
final Element element, final String resolvedTargetNamespace, final String prefix, final String tnsForPrefix) {
final boolean targetNamespacesAreTheSame = (tnsForPrefix != null && tnsForPrefix.equals(resolvedTargetNamespace));
// this is used to resolve referred elements from importing schemas
final boolean prefixValueMatchesResolvedTargetNamespace = (prefixesMap.get(prefix) != null && prefixesMap.get(prefix)
.equals(resolvedTargetNamespace));
return targetNamespacesAreTheSame && (/*
* element.getAttribute(tnsPrefixAttribute
* (prefix)) != null ||
*/prefixValueMatchesResolvedTargetNamespace);
}
// /**
// * Utility method. It builds the tns prefix attribute for the given
// prefix.
// * If the prefix is some custom prefix (for e.g. "ns1"), it returns
// "xmlns:"
// * + "ns1". <br>
// * <br>
// * If the prefix is the default one, it simply returns it.
// */
// private String tnsPrefixAttribute(final String prefix) {
// String prefixAttribute = null;
// if (EmfXsdUtils.XMLNS_PREFIX.equals(prefix)) {
// prefixAttribute = EmfXsdUtils.XMLNS_PREFIX;
// } else {
// prefixAttribute = EmfXsdUtils.XMLNS_PREFIX + ':' + prefix;
// }
// return prefixAttribute;
// }
private UpdateNSPrefixUtils prefixUtils() {
return UpdateNSPrefixUtils.instance();
}
}