/******************************************************************************* * Copyright (c) 2010 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.jsp.ui.internal.contentassist; import org.eclipse.core.resources.IResource; import org.eclipse.jst.jsp.core.internal.provisional.JSP11Namespace; import org.eclipse.jst.jsp.ui.internal.editor.JSPEditorPluginImageHelper; import org.eclipse.jst.jsp.ui.internal.editor.JSPEditorPluginImages; import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; import org.eclipse.wst.sse.core.utils.StringUtils; import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext; import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; import org.eclipse.wst.xml.ui.internal.contentassist.DefaultXMLCompletionProposalComputer; import org.eclipse.wst.xml.ui.internal.contentassist.XMLRelevanceConstants; import org.eclipse.wst.xml.ui.internal.util.SharedXMLEditorPluginImageHelper; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * <p>This class computes attribute value completion proposals * for <code><jsp:[gs]etProperty></code> tags.</p> */ public class JSPPropertyCompletionProposalComputer extends DefaultXMLCompletionProposalComputer { /** * @see org.eclipse.wst.xml.ui.internal.contentassist.DefaultXMLCompletionProposalComputer#addAttributeValueProposals(org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest, org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext) */ protected void addAttributeValueProposals( ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context) { IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); //only add attribute value proposals for specific elements if(node.getNodeName().equals(JSP11Namespace.ElementName.SETPROPERTY) || node.getNodeName().equals(JSP11Namespace.ElementName.GETPROPERTY)) { // Find the attribute name for which this position should have a value IStructuredDocumentRegion open = node.getFirstStructuredDocumentRegion(); ITextRegionList openRegions = open.getRegions(); int i = openRegions.indexOf(contentAssistRequest.getRegion()); if (i < 0) return; // get the attribute in question (first attr name to the left of the cursor) ITextRegion attrNameRegion = null; String attributeName = null; while (i >= 0) { attrNameRegion = openRegions.get(i--); if (attrNameRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) break; } if (attrNameRegion != null) attributeName = open.getText(attrNameRegion); // determine get or set ITextRegion tagNameRegion = null; boolean isGetProperty = true; for (int j = 0; j < openRegions.size(); j++) { tagNameRegion = openRegions.get(j); if (tagNameRegion.getType() == DOMRegionContext.XML_TAG_NAME && open.getText(tagNameRegion).trim().equals("jsp:setProperty")) { //$NON-NLS-1$ isGetProperty = false; break; } } String currentValue = null; if (contentAssistRequest.getRegion().getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) currentValue = contentAssistRequest.getText(); else currentValue = ""; //$NON-NLS-1$ String matchString = null; // fixups since the matchString computations don't care if there are quotes around the value if (currentValue.length() > StringUtils.strip(currentValue).length() && (currentValue.startsWith("\"") || currentValue.startsWith("'")) && //$NON-NLS-1$ //$NON-NLS-2$ contentAssistRequest.getMatchString().length() > 0) { matchString = currentValue.substring(1, contentAssistRequest.getMatchString().length()); } else { matchString = currentValue.substring(0, contentAssistRequest.getMatchString().length()); } // for now we ignore complicated values such as jsp embedded in an attribute boolean existingComplicatedValue = contentAssistRequest.getRegion() != null && contentAssistRequest.getRegion() instanceof ITextRegionContainer; if (existingComplicatedValue) { contentAssistRequest.getProposals().clear(); contentAssistRequest.getMacros().clear(); } else { if (attributeName.equals(JSP11Namespace.ATTR_NAME_NAME)) { addBeanNameProposals(contentAssistRequest, node, matchString); } else if (attributeName.equals(JSP11Namespace.ATTR_NAME_PROPERTY)) { addBeanPropertyProposals(contentAssistRequest, node, isGetProperty, matchString); } } } } /** * <p>Add bean property proposals to the given {@link ContentAssistRequest}</p> * * @param contentAssistRequest * @param node * @param isGetProperty * @param matchString */ private void addBeanPropertyProposals(ContentAssistRequest contentAssistRequest, IDOMNode node, boolean isGetProperty, String matchString) { if (((Element) node).hasAttribute(JSP11Namespace.ATTR_NAME_NAME)) { // assumes that the node is the [gs]etProperty tag String useBeanName = ((Element) node).getAttribute(JSP11Namespace.ATTR_NAME_NAME); // properties can only be provided if a class/type/beanName has been declared if (useBeanName.length() > 0) { NodeList useBeans = node.getOwnerDocument().getElementsByTagName(JSP11Namespace.ElementName.USEBEAN); if (useBeans != null) { String typeName = null; for (int j = 0; j < useBeans.getLength(); j++) { if (useBeans.item(j).getNodeType() != Node.ELEMENT_NODE) continue; Element useBean = (Element) useBeans.item(j); if (useBean instanceof IndexedRegion && ((IndexedRegion) useBean).getStartOffset() < node.getStartOffset()) { if (useBeanName.equals(useBean.getAttribute(JSP11Namespace.ATTR_NAME_ID))) { typeName = useBean.getAttribute(JSP11Namespace.ATTR_NAME_CLASS); if (!useBean.hasAttribute(JSP11Namespace.ATTR_NAME_CLASS) || typeName.length() < 1) { typeName = useBean.getAttribute(JSP11Namespace.ATTR_NAME_TYPE); } if (!useBean.hasAttribute(JSP11Namespace.ATTR_NAME_TYPE) || typeName.length() < 1) { typeName = useBean.getAttribute(JSP11Namespace.ATTR_NAME_BEAN_NAME); } } } } if (typeName != null && typeName.length() > 0) { // find the class/type/beanName definition and obtain the list of properties IBeanInfoProvider provider = new BeanInfoProvider(); IResource resource = JSPContentAssistHelper.getResource(contentAssistRequest); IJavaPropertyDescriptor[] descriptors = provider.getRuntimeProperties(resource, typeName); CustomCompletionProposal proposal = null; String displayString = ""; //$NON-NLS-1$ for (int j = 0; j < descriptors.length; j++) { IJavaPropertyDescriptor pd = descriptors[j]; // check whether it's get or set kinda property if (pd.getReadable() && isGetProperty || pd.getWriteable() && !isGetProperty) { // filter attr value name if (matchString.length() == 0 || pd.getName().toLowerCase().startsWith(matchString.toLowerCase())) { displayString = pd.getDisplayName(); if (pd.getDeclaredType() != null && pd.getDeclaredType().length() > 0) displayString += " - " + pd.getDeclaredType(); //$NON-NLS-1$ proposal = new CustomCompletionProposal("\"" + pd.getName() + "\"", //$NON-NLS-1$ //$NON-NLS-2$ contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), pd.getName().length() + 2, SharedXMLEditorPluginImageHelper.getImage(SharedXMLEditorPluginImageHelper.IMG_OBJ_ATTRIBUTE), displayString, null, pd.getDeclaredType(), XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); contentAssistRequest.addProposal(proposal); } } } } } } } } /** * <p>Add bean name propoasals to the given {@link ContentAssistRequest}</p> * * @param contentAssistRequest * @param node * @param matchString */ private void addBeanNameProposals(ContentAssistRequest contentAssistRequest, IDOMNode node, String matchString) { // will not catch useBeans specified using other than actual DOM Nodes NodeList useBeans = node.getOwnerDocument().getElementsByTagName(JSP11Namespace.ElementName.USEBEAN); if (useBeans != null) { String id = ""; //$NON-NLS-1$ String displayString = null; String classOrType = null; String imageName = JSPEditorPluginImages.IMG_OBJ_CLASS_OBJ; for (int j = 0; j < useBeans.getLength(); j++) { if (useBeans.item(j).getNodeType() != Node.ELEMENT_NODE) { continue; } Element useBean = (Element) useBeans.item(j); if (useBean instanceof IndexedRegion && ((IndexedRegion) useBean).getStartOffset() < node.getStartOffset() && useBean.hasAttribute(JSP11Namespace.ATTR_NAME_ID)) { id = useBean.hasAttribute(JSP11Namespace.ATTR_NAME_ID) ? StringUtils.strip(useBean.getAttribute(JSP11Namespace.ATTR_NAME_ID)) : null; displayString = null; classOrType = null; imageName = JSPEditorPluginImages.IMG_OBJ_CLASS_OBJ; // set the Image based on whether the class, type, or beanName attribute is present if (useBean.hasAttribute(JSP11Namespace.ATTR_NAME_CLASS)) classOrType = useBean.getAttribute(JSP11Namespace.ATTR_NAME_CLASS); if ((classOrType == null || classOrType.length() < 1) && useBean.hasAttribute(JSP11Namespace.ATTR_NAME_TYPE)) { classOrType = useBean.getAttribute(JSP11Namespace.ATTR_NAME_TYPE); imageName = JSPEditorPluginImages.IMG_OBJ_PUBLIC; } if ((classOrType == null || classOrType.length() < 1) && useBean.hasAttribute(JSP11Namespace.ATTR_NAME_BEAN_NAME)) { classOrType = useBean.getAttribute(JSP11Namespace.ATTR_NAME_BEAN_NAME); imageName = JSPEditorPluginImages.IMG_OBJ_PUBLIC; } if (classOrType != null && classOrType.length() > 0) { displayString = id + " - " + classOrType; //$NON-NLS-1$ } else { displayString = id; } // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=2341 if(id != null) { // filter if (matchString.length() == 0 || id.startsWith(matchString)) { CustomCompletionProposal proposal = new CustomCompletionProposal("\"" + id + "\"", //$NON-NLS-1$ //$NON-NLS-2$ contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), id.length() + 2, JSPEditorPluginImageHelper.getInstance().getImage(imageName), displayString, null, null, XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); contentAssistRequest.addProposal(proposal); } } } } } } }