blob: 1018f8e4bb67f6a8ce04fb09f98a2902276bb778 [file] [log] [blame]
/*******************************************************************************
* 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>&lt;jsp:[gs]etProperty&gt;</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);
}
}
}
}
}
}
}