| package org.eclipse.jst.jsf.facelet.ui.internal.contentassist; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.jface.text.ITextViewer; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.Namespace; |
| 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.IWorkspaceContextResolver; |
| import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext; |
| import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContextFactory; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry; |
| import org.eclipse.jst.jsf.facelet.core.internal.cm.FaceletDocumentFactory; |
| import org.eclipse.jst.jsf.facelet.core.internal.util.ViewUtil; |
| import org.eclipse.jst.jsf.facelet.core.internal.util.ViewUtil.PrefixEntry; |
| 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.ITextRegionList; |
| import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; |
| 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.AbstractContentAssistProcessor; |
| import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; |
| import org.eclipse.wst.xml.ui.internal.contentassist.XMLRelevanceConstants; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| |
| /** |
| * The main entry point for Facelet content assist in html files. |
| * @author cbateman |
| * |
| */ |
| public class XHTMLContentAssistProcessor extends AbstractContentAssistProcessor |
| { |
| private IFile _file; |
| private FaceletDocumentFactory _factory; |
| |
| private final static ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0]; |
| |
| @Override |
| public ICompletionProposal[] computeCompletionProposals( |
| final ITextViewer textViewer, final int documentPosition) |
| { |
| ICompletionProposal[] proposals = null; |
| _file = getFile(textViewer, documentPosition); |
| |
| if (_file != null && shouldContribute(_file)) |
| { |
| _factory = new FaceletDocumentFactory(_file.getProject()); |
| proposals = super.computeCompletionProposals(textViewer, |
| documentPosition); |
| } |
| return proposals != null ? proposals : NO_PROPOSALS; |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| protected List getAvailableChildElementDeclarations(final Element parent, |
| final int childPosition, final int kindOfAction) |
| { |
| final Map<String, PrefixEntry> namespaces = getDocumentNamespaces( |
| _factory, childPosition); |
| final List availableChildElements = new ArrayList(); |
| |
| for (final Map.Entry<String, PrefixEntry> entry : namespaces.entrySet()) |
| { |
| final String prefix = entry.getValue().getPrefix(); |
| final CMDocument cmDocument = _factory.createCMDocumentForContext( |
| entry.getValue().getUri(), prefix); |
| |
| if (cmDocument != null) |
| { |
| final Iterator it = cmDocument.getElements().iterator(); |
| while (it.hasNext()) |
| { |
| availableChildElements.add(it.next()); |
| } |
| } |
| } |
| |
| return availableChildElements; |
| } |
| |
| @Override |
| protected void addAttributeValueProposals( |
| final ContentAssistRequest contentAssistRequest) |
| { |
| final ITextRegion textRegion = findNameRegionIfIsHTMLRoot(contentAssistRequest); |
| |
| if (textRegion != null) |
| { |
| final IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); |
| final NamedNodeMap attributes = node.getAttributes(); |
| final String attrName = node.getFirstStructuredDocumentRegion() |
| .getText(textRegion); |
| if (attrName != null) |
| { |
| final int colonPos = attrName.indexOf(':'); |
| // must have a colon && it must not be the last char, otherwise |
| // there is no localName |
| if (colonPos > -1 && colonPos < attrName.length() - 1) |
| { |
| final String prefix = attrName.substring(0, colonPos); |
| |
| if ("xmlns".equals(prefix)) |
| { |
| final ITagRegistry tagRegistry = ViewUtil.getHtmlTagRegistry(_file.getProject()); |
| if (tagRegistry != null) |
| { |
| final Set<Attr> alreadyUsed = ViewUtil.getDeclaredNamespaces(attributes); |
| final Collection<? extends Namespace> namespaces = tagRegistry |
| .getAllTagLibraries(); |
| NAMESPACE_LOOP: for (final Namespace ns : namespaces) |
| { |
| final String possibleValue = ns.getNSUri(); |
| |
| if (ViewUtil.hasAttributeValue(alreadyUsed, possibleValue)) |
| { |
| continue NAMESPACE_LOOP; |
| } |
| // we have an attribute of the form xmlns:X in |
| // the |
| // html root node. Here we can provide value |
| // proposals |
| // for all of the known namespaces. |
| final String rString = "\"" + possibleValue + "\""; //$NON-NLS-2$//$NON-NLS-1$ |
| final int rOffset = contentAssistRequest |
| .getReplacementBeginPosition(); |
| final int rLength = contentAssistRequest |
| .getReplacementLength(); |
| final int cursorAfter = possibleValue.length() + 1; |
| final String displayString = "\"" + possibleValue + "\""; //$NON-NLS-2$//$NON-NLS-1$ |
| |
| final CustomCompletionProposal proposal = new CustomCompletionProposal( |
| rString, |
| rOffset, |
| rLength, |
| cursorAfter, |
| null, |
| displayString, |
| null, |
| null, |
| XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| // now bail, since super only adds annoying identity completions |
| // for this case |
| return; |
| } |
| } |
| } |
| } |
| super.addAttributeValueProposals(contentAssistRequest); |
| } |
| |
| private ITextRegion findNameRegionIfIsHTMLRoot( |
| final ContentAssistRequest contentAssistRequest) |
| { |
| final IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); |
| if (node.getNodeType() == Node.ELEMENT_NODE |
| && "html".equals(node.getNodeName()) |
| && node.getOwnerDocument().getDocumentElement() == node) |
| { |
| // Find the attribute region and name for which this position should |
| // have a value proposed |
| final IStructuredDocumentRegion open = node |
| .getFirstStructuredDocumentRegion(); |
| final ITextRegionList openRegions = open.getRegions(); |
| int i = openRegions.indexOf(contentAssistRequest.getRegion()); |
| if (i < 0) |
| { |
| return null; |
| } |
| ITextRegion nameRegion = null; |
| while (i >= 0) |
| { |
| nameRegion = openRegions.get(i--); |
| if (nameRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) |
| { |
| break; |
| } |
| } |
| return nameRegion; |
| } |
| return null; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Map<String, PrefixEntry> getDocumentNamespaces( |
| final FaceletDocumentFactory factory, final int offset) |
| { |
| final IStructuredDocumentContext context = IStructuredDocumentContextFactory.INSTANCE |
| .getContext(fTextViewer, offset); |
| |
| Document doc = null; |
| if (context != null) |
| { |
| final IDOMContextResolver domContextResolver = IStructuredDocumentContextResolverFactory.INSTANCE |
| .getDOMContextResolver(context); |
| |
| doc = domContextResolver.getDOMDocument(); |
| |
| if (doc == null) |
| { |
| return Collections.EMPTY_MAP; |
| } |
| } |
| return ViewUtil.getDocumentNamespaces(doc); |
| } |
| |
| @Override |
| protected CMElementDeclaration getCMElementDeclaration(final Node node) |
| { |
| if (node.getNodeType() == Node.ELEMENT_NODE) |
| { |
| if (node.getPrefix() != null) |
| { |
| final Element element = (Element) node; |
| |
| final CMElementDeclaration elementDecl = _factory |
| .createCMElementDeclaration(element); |
| |
| if (elementDecl != null) |
| { |
| return elementDecl; |
| } |
| } |
| } |
| |
| return null; |
| // return super.getCMElementDeclaration(node); |
| } |
| |
| private boolean shouldContribute(final IFile file) |
| { |
| return ViewUtil.isFaceletVDLFile(file); |
| } |
| |
| private IFile getFile(final ITextViewer textViewer, |
| final int documentPosition) |
| { |
| final IStructuredDocumentContext context = IStructuredDocumentContextFactory.INSTANCE |
| .getContext(textViewer, documentPosition); |
| |
| if (context != null) |
| { |
| final IWorkspaceContextResolver resolver = IStructuredDocumentContextResolverFactory.INSTANCE |
| .getWorkspaceContextResolver(context); |
| |
| if (resolver != null) |
| { |
| final IResource resource = resolver.getResource(); |
| if (resource != null && |
| resource.getType() == IResource.FILE) |
| { |
| return (IFile) resource; |
| } |
| } |
| } |
| return null; |
| } |
| } |