/******************************************************************************* * Copyright (c) 2007, 2009 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.ui.editor.contentassist; import java.util.HashMap; import java.util.Map; import org.eclipse.jface.text.contentassist.IContentAssistProcessor; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; import org.springframework.ide.eclipse.beans.core.namespaces.NamespaceUtils; import org.springframework.ide.eclipse.beans.ui.editor.namespaces.INamespaceContentAssistProcessor; import org.springframework.util.StringUtils; import org.w3c.dom.Node; /** * Support class for implementing custom {@link IContentAssistProcessor}. Calculation of individual * content assist proposals is done via {@link IContentAssistCalculator} strategy interfaces * respectively. * <p> * Provides the {@link #registerContentAssistCalculator} methods for registering a * {@link IContentAssistCalculator} to handle a specific element. * @author Christian Dupuis * @since 2.0.2 */ @SuppressWarnings("restriction") public abstract class NamespaceContentAssistProcessorSupport extends AbstractContentAssistProcessor implements INamespaceContentAssistProcessor { /** * Stores the {@link IContentAssistCalculator} keyed the return value of a call to * {@link #createRegisteredName(String, String)}. */ private Map<String, IContentAssistCalculator> calculators = new HashMap<String, IContentAssistCalculator>(); /** * Empty implementation. Can be overridden by subclasses. */ @Override protected void computeAttributeNameProposals(ContentAssistRequest request, String prefix, String namespace, String namespacePrefix, Node attributeNode) { // no-op } /** * Empty implementation. Can be overridden by subclasses. */ @Override protected void computeTagInsertionProposals(ContentAssistRequest request, IDOMNode node) { // no-op } /** * Calculates content assist proposals for the given request by delegating the request to a * located {@link IContentAssistCalculator} returned by * {@link #locateContentAssistCalculator(String, String)}. * <p> * After delegating the calculation to a {@link IContentAssistCalculator} this implementation * calls {@link #postComputeAttributeValueProposals} to allow for custom post processing. */ @Override protected final void computeAttributeValueProposals(ContentAssistRequest request, IDOMNode node, String matchString, String attributeName, String namespace, String prefix) { if (matchString == null) { matchString = ""; } String parentNodeName = null; String parentNamespaceUri = null; IDOMNode parentNode = (IDOMNode) node.getParentNode(); if (parentNode != null) { parentNodeName = parentNode.getLocalName(); parentNamespaceUri = parentNode.getNamespaceURI(); } // make sure the for old-style DTDs we assume the default namespace if (parentNamespaceUri == null) { parentNamespaceUri = NamespaceUtils.DEFAULT_NAMESPACE_URI; } IContentAssistCalculator calculator = locateContentAssistCalculator(parentNamespaceUri, parentNodeName, node.getLocalName(), attributeName); IContentAssistContext context = new DefaultContentAssistContext(request, attributeName, matchString); IContentAssistProposalRecorder recorder = new DefaultContentAssistProposalRecorder(request); if (calculator != null) { calculator.computeProposals(context, recorder); } postComputeAttributeValueProposals(request, node, matchString, attributeName, namespace, prefix); } /** * Template method called after delegating the content assist request to a stored * {@link IContentAssistCalculator}. This method can be overridden by subclasses to allow custom * handling of requests. * @param request the content assist request * @param node the current node * @param matchString the string already entered by the user prior to triggering the content * assist request * @param attributeName the name of the attribute * @param namespace the namespace of the attribute * @param prefix the namespace prefix of the attribute */ protected void postComputeAttributeValueProposals(ContentAssistRequest request, IDOMNode node, String matchString, String attributeName, String namespace, String prefix) { } /** * Locates a {@link IContentAssistCalculator} in the {@link #calculators} store for the given * <code>nodeName</code> and <code>attributeName</code>. */ private IContentAssistCalculator locateContentAssistCalculator(String parentNamespaceUri, String parentNodeName, String nodeName, String attributeName) { String key = createRegisteredName(parentNamespaceUri, parentNodeName, nodeName, attributeName); if (this.calculators.containsKey(key)) { return this.calculators.get(key); } key = createRegisteredName(null, null, nodeName, attributeName); if (this.calculators.containsKey(key)) { return this.calculators.get(key); } key = createRegisteredName(null, null, null, attributeName); if (this.calculators.containsKey(key)) { return this.calculators.get(key); } return null; } /** * Creates a name from the <code>nodeName</code> and <code>attributeName</code>. * @param parentNamespaceUri the namespace uri of the parent node * @param parentNodeName the local name of the parent node * @param nodeName the local (non-namespace qualified) name of the element * @param attributeName the local (non-namespace qualified) name of the attribute */ protected String createRegisteredName(String parentNamespaceUri, String parentNodeName, String nodeName, String attributeName) { StringBuilder builder = new StringBuilder(); if (StringUtils.hasText(parentNamespaceUri)) { builder.append("/parentNamespaceUri="); builder.append(parentNamespaceUri); } else { builder.append("/parentNamespaceUri="); builder.append("*"); } if (StringUtils.hasText(parentNodeName)) { builder.append("/parentNodeName="); builder.append(parentNodeName); } else { builder.append("/parentNodeName="); builder.append("*"); } if (StringUtils.hasText(nodeName)) { builder.append("/nodeName="); builder.append(nodeName); } else { builder.append("/nodeName="); builder.append("*"); } if (StringUtils.hasText(attributeName)) { builder.append("/attribute="); builder.append(attributeName); } return builder.toString(); } /** * Subclasses can call this to register the supplied {@link IContentAssistCalculator} to handle * the specified attribute. The attribute name is the local (non-namespace qualified) name. */ protected void registerContentAssistCalculator(String attributeName, IContentAssistCalculator calculator) { registerContentAssistCalculator(null, null, null, attributeName, calculator); } /** * Subclasses can call this to register the supplied {@link IContentAssistCalculator} to handle * the specified attribute. The attribute name is the local (non-namespace qualified) name. */ protected void registerContentAssistCalculator(String nodeName, String attributeName, IContentAssistCalculator calculator) { registerContentAssistCalculator(null, null, nodeName, attributeName, calculator); } /** * Subclasses can call this to register the supplied {@link IContentAssistCalculator} to handle * the specified attribute <b>only</b> for a given element. The attribute name is the local * (non-namespace qualified) name. */ protected void registerContentAssistCalculator(String parentNamespaceUri, String parentNodeName, String nodeName, String attributeName, IContentAssistCalculator calculator) { this.calculators.put(createRegisteredName(parentNamespaceUri, parentNodeName, nodeName, attributeName), calculator); } }