/******************************************************************************* * Copyright (c) 2008, 2010 Spring IDE Developers * 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: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model.namespaces; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver; import org.springframework.beans.factory.xml.NamespaceHandler; import org.springframework.beans.factory.xml.NamespaceHandlerResolver; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.ide.eclipse.beans.core.BeansCorePlugin; import org.springframework.ide.eclipse.beans.core.internal.model.ToolAnnotationBasedNamespaceHandler; import org.springframework.ide.eclipse.beans.core.internal.model.namespaces.DocumentAccessor.SchemaLocations; import org.springframework.ide.eclipse.beans.core.model.IBeansConfig; import org.springframework.ide.eclipse.beans.core.namespaces.NamespaceUtils; import org.springframework.ide.eclipse.beans.core.namespaces.NamespaceUtils.NamespaceHandlerDescriptor; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * This {@link NamespaceHandlerResolver} provides a {@link NamespaceHandler} for a given namespace URI. Depending on * this namespace URI the returned namespace handler is one of the following (in the provided order): * <ol> * <li>a namespace handler provided by the Spring framework</li> * <li>a namespace handler contributed via the extension point * <code>org.springframework.ide.eclipse.beans.core.namespaces</code></li> * <li>a namespace handler resolved by a NemespaceHandlerResolver published as OSGi service</li> * <li>a {@link ToolAnnotationBasedNamespaceHandler}</li> * </ol> * * * @author Christian Dupuis * @author Torsten Juergeleit * @since 2.0.3 */ public class DelegatingNamespaceHandlerResolver extends DefaultNamespaceHandlerResolver { private static final SchemaLocations EMPTY_SCHEMA_LOCATIONS = new SchemaLocations(); private NamespaceHandler toolAnnotationNamespaceHandler; private final Map<NamespaceHandlerDescriptor, NamespaceHandler> namespaceHandlers; private final Set<NamespaceHandlerResolver> namespaceHandlerResolvers; private final DocumentAccessor documentAccessor; private final Map<String, NamespaceHandler> resolvedNamespaceHandlers = new HashMap<String, NamespaceHandler>(); public DelegatingNamespaceHandlerResolver(ClassLoader classLoader, IBeansConfig beansConfig) { this(classLoader, beansConfig, null); } public DelegatingNamespaceHandlerResolver(ClassLoader classLoader, IBeansConfig beansConfig, DocumentAccessor documentHolder) { super(classLoader); this.documentAccessor = documentHolder; this.namespaceHandlers = NamespaceUtils.getNamespaceHandlers(); this.namespaceHandlerResolvers = NamespaceUtils.getNamespaceHandlerResolvers(); if (beansConfig != null) { this.toolAnnotationNamespaceHandler = new ToolAnnotationBasedNamespaceHandler(beansConfig); } } @Override public NamespaceHandler resolve(String namespaceUri) { // Check cache first if (resolvedNamespaceHandlers.containsKey(namespaceUri)) { return resolvedNamespaceHandlers.get(namespaceUri); } NamespaceHandler namespaceHandler = null; try { // First check for a namespace handler provided by Spring. namespaceHandler = super.resolve(namespaceUri); if (namespaceHandler != null) { return decorateNamespaceHandler(namespaceHandler); } SchemaLocations schemaLocations = EMPTY_SCHEMA_LOCATIONS; if (documentAccessor != null) { schemaLocations = documentAccessor.getCurrentSchemaLocations(); } // Then check for a namespace handler contributed for the specific schemalocation String schemaLocation = schemaLocations.getSchemaLocation(namespaceUri); if (schemaLocation != null) { namespaceHandler = namespaceHandlers.get(NamespaceHandlerDescriptor.createNamespaceHandlerDescriptor( namespaceUri, schemaLocation)); if (namespaceHandler != null) { return decorateNamespaceHandler(namespaceHandler); } } // Then check for a namespace handler provided by an extension. namespaceHandler = namespaceHandlers.get(NamespaceHandlerDescriptor.createNamespaceHandlerDescriptor( namespaceUri, null)); if (namespaceHandler != null) { return decorateNamespaceHandler(namespaceHandler); } // Then check the contributed NamespaceHandlerResolver. for (NamespaceHandlerResolver resolver : namespaceHandlerResolvers) { try { namespaceHandler = resolver.resolve(namespaceUri); if (namespaceHandler != null) { return decorateNamespaceHandler(namespaceHandler); } } catch (Exception e) { // Make sure a contributed NamespaceHandlerResolver can't prevent parsing. BeansCorePlugin.log(e); } } // Finally fall back to the tool annotation based namespace handler. return toolAnnotationNamespaceHandler; } finally { // Add to cache for subsequent faster access if (namespaceHandler != null) { if (!(namespaceHandler instanceof ElementTrackingNamespaceHandler)) { namespaceHandler = decorateNamespaceHandler(namespaceHandler); } resolvedNamespaceHandlers.put(namespaceUri, namespaceHandler); } } } /** * Decorate the given {@link NamespaceHandler} with an {@link ElementTrackingNamespaceHandler}. */ private NamespaceHandler decorateNamespaceHandler(NamespaceHandler namespaceHandler) { return new ElementTrackingNamespaceHandler(namespaceHandler); } /** * {@link NamespaceHandler} that wraps another instance and keeps track of the current {@link Element} being parsed * or decorated. * @since 2.5.2 */ class ElementTrackingNamespaceHandler implements NamespaceHandler { private final NamespaceHandler namespaceHandler; public ElementTrackingNamespaceHandler(NamespaceHandler namespaceHandler) { this.namespaceHandler = namespaceHandler; } /** * {@inheritDoc} */ public void init() { namespaceHandler.init(); } /** * {@inheritDoc} */ public BeanDefinition parse(Element element, ParserContext parserContext) { try { documentAccessor.pushElement(element); return namespaceHandler.parse(element, parserContext); } finally { documentAccessor.popElement(); } } /** * {@inheritDoc} */ public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext) { try { documentAccessor.pushElement(source); return namespaceHandler.decorate(source, definition, parserContext); } finally { documentAccessor.popElement(); } } } }