blob: a786371a1f19bf33d525ccc69224e9252d49a9ce [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Oracle Corporation.
* 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:
* Gerry Kessler/Oracle - initial API and implementation
*
********************************************************************************/
package org.eclipse.jst.jsf.ui.internal.contentassist;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jst.jsf.context.resolver.structureddocument.IDOMContextResolver;
import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory;
import org.eclipse.jst.jsf.context.resolver.structureddocument.ITaglibContextResolver;
import org.eclipse.jst.jsf.context.resolver.structureddocument.internal.IStructuredDocumentContextResolverFactory2;
import org.eclipse.jst.jsf.context.resolver.structureddocument.internal.ITextRegionContextResolver;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContextFactory;
import org.eclipse.jst.jsf.metadataprocessors.MetaDataEnabledProcessingFactory;
import org.eclipse.jst.jsf.metadataprocessors.features.IPossibleValue;
import org.eclipse.jst.jsf.metadataprocessors.features.IPossibleValues;
import org.eclipse.jst.jsf.ui.internal.JSFUiPlugin;
import org.eclipse.swt.graphics.Image;
import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;
import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery;
import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.eclipse.wst.xml.ui.internal.contentassist.XMLRelevanceConstants;
import org.eclipse.wst.xml.ui.internal.taginfo.MarkupTagInfoProvider;
import org.osgi.framework.Bundle;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* The content assist processor for non-EL attribute values.
*
* @author Gerry Kessler - Oracle
*
*/
public class JSFContentAssistProcessor implements IContentAssistProcessor {
private ITextRegionContextResolver resolver;
private ITaglibContextResolver tlResolver;
private String defaultAdditionalInfo;
private String defaultIconPath = "/icons/attr_val.gif"; //$NON-NLS-1$
private ImageDescriptor defaultAttrValImgDesc;
private MarkupTagInfoProvider fInfoProvider;
/**
* @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer,
* int)
*/
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
int documentPosition) {
List proposals = new ArrayList();
IStructuredDocumentContext context = IStructuredDocumentContextFactory.INSTANCE
.getContext(viewer, documentPosition);
if (context != null) {
resolver = IStructuredDocumentContextResolverFactory.INSTANCE
.getTextRegionResolver(context);
if (resolver != null) {
final String regionType = resolver.getRegionType();
if (regionType != null
&& regionType.equals(DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)) {
tlResolver = IStructuredDocumentContextResolverFactory2.INSTANCE
.getTaglibContextResolverFromDelegates(context);
if (tlResolver != null) {
Attr attr = getAttribute(context);
if (attr != null) {
Node tagElement = attr.getOwnerElement();
if (tagElement != null) {
String uri = tlResolver.getTagURIForNodeName(tagElement);
if (uri != null) {
proposals = createProposals(context, uri, tagElement, attr);
}
}
}
}
}
}
}
return (ICompletionProposal[]) proposals
.toArray(new ICompletionProposal[0]);
}
private String getDefaultAdditionalInfo(Node tagElement, Attr attr) {
if (defaultAdditionalInfo == null){
CMElementDeclaration elementNode = getCMElementDeclaration(tagElement);
if (elementNode != null){
CMAttributeDeclaration attrNode = getCMAttributeDeclaration(elementNode, attr);
if (attrNode != null)
defaultAdditionalInfo = getInfoProvider().getInfo(attrNode);
}
}
return defaultAdditionalInfo;
}
private CMElementDeclaration getCMElementDeclaration(Node node) {
CMElementDeclaration result = null;
if (node.getNodeType() == Node.ELEMENT_NODE) {
ModelQuery modelQuery = ModelQueryUtil.getModelQuery(node.getOwnerDocument());
if (modelQuery != null)
result = modelQuery.getCMElementDeclaration((Element) node);
}
return result;
}
private CMAttributeDeclaration getCMAttributeDeclaration(CMElementDeclaration tagElement, Attr attr) {
CMNamedNodeMap attrs = tagElement.getAttributes();
for (Iterator it = attrs.iterator();it.hasNext();){
CMAttributeDeclaration CMAttr = (CMAttributeDeclaration)it.next();
if (CMAttr.getAttrName().equals(attr.getName()))
return CMAttr;
}
return null;
}
private MarkupTagInfoProvider getInfoProvider() {
if (fInfoProvider == null) {
fInfoProvider = new MarkupTagInfoProvider();
}
return fInfoProvider;
}
private List createProposals(IStructuredDocumentContext context, String uri, Node tagElement, Attr attr) {
List ret = new ArrayList();
List processors = MetaDataEnabledProcessingFactory.getInstance()
.getAttributeValueRuntimeTypeFeatureProcessors(
IPossibleValues.class, context, uri,
tagElement.getLocalName(), attr.getLocalName());
if (processors != null) {
for (int i = 0; i < processors.size(); i++) {
IPossibleValues p = (IPossibleValues) processors.get(i);
ret.addAll(createProposals(p, tagElement, attr));
}
}
return ret;
}
private List createProposals(IPossibleValues p, Node tagElement, Attr attr) {
List ret = new ArrayList();
defaultAdditionalInfo = null;
Iterator it = p.getPossibleValues().iterator();
while (it.hasNext()) {
IPossibleValue val = (IPossibleValue) it.next();
if (val != null){ //just in case...
ICompletionProposal proposal = new CustomCompletionProposal(
val.getValue(), //replacement text
getReplacementOffset(), //replacementOffset
getReplacementLength(), //replacementLength
getCursorPosition(val), //cursor pos
getImage(val), //image
val.getDisplayValue(), //display value
null, //IContextInformation
getAdditionalInfo(val,tagElement,attr), //addditional info
XMLRelevanceConstants.R_JSP_ATTRIBUTE_VALUE, //relevance
true); //updateReplace
ret.add(proposal);
}
}
return ret;
}
private String getAdditionalInfo(IPossibleValue val, Node tagElement, Attr attr) {
if (val.getAdditionalInformation() != null)
return val.getAdditionalInformation();
return getDefaultAdditionalInfo(tagElement, attr);
}
private int getReplacementLength() {
return resolver.getRegionText().length() - 2;
}
private int getCursorPosition(IPossibleValue val) {
//Something changed in 1.5RC2 CustomCompletionProposal
//it appears that the cursor position is now relative to where it is currently
//rather than relative to document
//return getReplacementOffset() + val.getValue().length();
return val.getValue().length();
}
private int getReplacementOffset() {
return resolver.getStartOffset() + 1;
}
private Image getImage(IPossibleValue val) {
ImageDescriptor icon = val.getIcon();
if (icon == null || icon.equals("")){ //$NON-NLS-1$
return getDefaultAttributeValueImage();
}
return getOrCreateImage(icon);
}
private Image getOrCreateImage(ImageDescriptor icon) {
if (icon == null)
return null;
Image img = JSFUiPlugin.getDefault().getImageRegistry().get(icon.toString());
if (img == null){
try {
img = icon.createImage();
JSFUiPlugin.getDefault().getImageRegistry().put(icon.toString(), img);
} catch (RuntimeException e) {
// empty block; C.B: handle exception?
}
}
return img;
}
private Image getDefaultAttributeValueImage() {
if (defaultAttrValImgDesc == null){
Bundle bundle = Platform.getBundle(JSFUiPlugin.PLUGIN_ID);
URL url= FileLocator.find(bundle,new Path(defaultIconPath ), null);
defaultAttrValImgDesc = ImageDescriptor.createFromURL(url);
}
return getOrCreateImage(defaultAttrValImgDesc);
}
private Attr getAttribute(IStructuredDocumentContext context) {
final IDOMContextResolver domResolver = IStructuredDocumentContextResolverFactory.INSTANCE
.getDOMContextResolver(context);
if (domResolver != null) {
final Node curNode = domResolver.getNode();
if (curNode instanceof Attr) {
return (Attr) curNode;
}
}
return null;
}
public IContextInformation[] computeContextInformation(ITextViewer viewer,
int offset) {
// no context info
return null;
}
public char[] getCompletionProposalAutoActivationCharacters() {
// auto activate when user hits a '.'
return new char[] { '.' };
}
public char[] getContextInformationAutoActivationCharacters() {
// no auto-activation for context info
return null;
}
public String getErrorMessage() {
// don't flag errors
return null;
}
public IContextInformationValidator getContextInformationValidator() {
// don't validate context information
return null;
}
}