/******************************************************************************* * Copyright (c) 2008 - 2013 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.tool; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.IType; import org.springframework.ide.eclipse.beans.core.namespaces.ToolAnnotationUtils; import org.springframework.ide.eclipse.beans.core.namespaces.ToolAnnotationUtils.ToolAnnotationData; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.BeanReferenceContentAssistCalculator; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.ClassContentAssistCalculator; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.ClassHierachyContentAssistCalculator; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.IAnnotationBasedContentAssistProcessor; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.IContentAssistCalculator; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.IContentAssistContext; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.IContentAssistProposalRecorder; import org.springframework.ide.eclipse.beans.ui.editor.contentassist.MethodContentAssistCalculator; import org.springframework.ide.eclipse.beans.ui.editor.util.BeansEditorUtils; import org.springframework.ide.eclipse.beans.ui.editor.util.BeansJavaCompletionUtils; import org.springframework.ide.eclipse.core.java.JdtUtils; import org.w3c.dom.Node; /** * {@link IAnnotationBasedContentAssistProcessor} that calculates content assist proposals based on Spring core's tool * namespace annotations. * <p> * Adding the following annotation will trigger a bean reference content assist search: * <pre> * <tool:annotation kind="ref"> * <tool:expected-type type="org.springframework.aop.Pointcut"/> * </tool:annotation> * </pre> * <p> * The following will launch the class, package and interface content assist. Depending on the assignable-to value that * proposals might be further narrowed to those implementing the interface specified: * <pre> * <tool:annotation> * <tool:expected-type type="java.lang.Class"/> * <tool:assignable-to type="java.util.List"/> * </tool:annotation> * </pre> * @author Christian Dupuis * @since 2.0.3 */ // TODO CD add support for restriction (class, interface, both) public class ToolAnnotationBasedContentAssistProcessor implements IAnnotationBasedContentAssistProcessor { private static final String REF_ATTRIBUTE = "ref"; private static final IContentAssistCalculator BEAN_REFERENCE_CALCULATOR = new BeanReferenceContentAssistCalculator( true); /** * {@inheritDoc} */ public void addAttributeValueProposals(IContentAssistContext context, IContentAssistProposalRecorder recorder, Node annotation) { if (ToolAnnotationUtils.ANNOTATION_ELEMENT.equals(annotation.getLocalName()) && ToolAnnotationUtils.TOOL_NAMESPACE_URI.equals(annotation.getNamespaceURI())) { ToolAnnotationData annotationData = ToolAnnotationUtils.getToolAnnotationData(annotation); if (REF_ATTRIBUTE.equals(annotationData.getKind())) { // bean reference content assist // TODO CD: add support for typed reference content assist BEAN_REFERENCE_CALCULATOR.computeProposals(context, recorder); } if (Class.class.getName().equals(annotationData.getExpectedType())) { // class content assist if (annotationData.getAssignableTo() == null) { getClassContentAssistCalculator(annotationData).computeProposals(context, recorder); } else { getClassHierachyContentAssistCalculator(annotationData).computeProposals(context, recorder); } } if (annotationData.getExpectedMethodType() != null) { String className = evaluateXPathExpression(annotationData.getExpectedMethodType(), context.getNode()); new NonFilteringMethodContentAssistCalculator(className).computeProposals(context, recorder); } else if (annotationData.getExpectedMethodRef() != null) { String typeName = evaluateXPathExpression(annotationData.getExpectedMethodRef(), context.getNode()); String className = BeansEditorUtils.getClassNameForBean(context.getFile(), context.getDocument(), typeName); new NonFilteringMethodContentAssistCalculator(className).computeProposals(context, recorder); } } } /** * {@inheritDoc} */ public void init() { // nothing to do } protected String evaluateXPathExpression(String xpath, Node node) { XPathFactory factory = XPathFactory.newInstance(); XPath path = factory.newXPath(); try { return path.evaluate(xpath, node); } catch (XPathExpressionException e) { return null; } } protected IContentAssistCalculator getClassContentAssistCalculator(ToolAnnotationData annotationData) { if ("class-only".equals(annotationData.getAssignableToRestriction())) { return new ClassContentAssistCalculator(false); } else if ("interface-only".equals(annotationData.getAssignableToRestriction())) { return new ClassContentAssistCalculator(true); } return new ClassContentAssistCalculator(); } protected IContentAssistCalculator getClassHierachyContentAssistCalculator(ToolAnnotationData annotationData) { if ("class-only".equals(annotationData.getAssignableToRestriction())) { return new ClassHierachyContentAssistCalculator(annotationData.getAssignableTo(), BeansJavaCompletionUtils.FLAG_CLASS); } else if ("interface-only".equals(annotationData.getAssignableToRestriction())) { return new ClassHierachyContentAssistCalculator(annotationData.getAssignableTo(), BeansJavaCompletionUtils.FLAG_INTERFACE); } return new ClassHierachyContentAssistCalculator(annotationData.getAssignableTo()); } class NonFilteringMethodContentAssistCalculator extends MethodContentAssistCalculator { private final String className; public NonFilteringMethodContentAssistCalculator(String className) { super(null); this.className = className; } /** * {@inheritDoc} */ @Override protected IType calculateType(IContentAssistContext context) { IFile file = context.getFile(); if (file != null && file.exists()) { return JdtUtils.getJavaType(file.getProject(), className); } return null; } } }