/******************************************************************************* * 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 java.util.Iterator; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts; import org.eclipse.jst.jsp.ui.internal.Logger; 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.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext; import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils; 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.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * <p>Computes JSP specific proposals</p> * * @base org.eclipse.jst.jsp.ui.internal.contentassist.JSPContentAssistProcessor */ public class JSPCompletionProposalComputer extends DefaultXMLCompletionProposalComputer { /** * <p>Create the computer</p> */ public JSPCompletionProposalComputer() { } /** * @see org.eclipse.wst.xml.ui.internal.contentassist.AbstractXMLCompletionProposalComputer#computeCompletionProposals(java.lang.String, org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion, org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode, org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode, org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext) */ protected ContentAssistRequest computeCompletionProposals(String matchString, ITextRegion completionRegion, IDOMNode treeNode, IDOMNode xmlnode, CompletionProposalInvocationContext context) { //be sure to get the super proposals ContentAssistRequest request = super.computeCompletionProposals(matchString, completionRegion, treeNode, xmlnode, context); //calculate JSP specific proposals int documentPosition = context.getInvocationOffset(); IStructuredDocumentRegion sdRegion = ContentAssistUtils.getStructuredDocumentRegion( context.getViewer(), documentPosition); Document doc = null; if (xmlnode != null) { if (xmlnode.getNodeType() == Node.DOCUMENT_NODE) { doc = (Document) xmlnode; } else { doc = xmlnode.getOwnerDocument(); } } String[] directiveNames = {"page", "include", "taglib"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ ITextRegion prevTextRegion = null; try { int offset = sdRegion.getStartOffset(completionRegion); if(offset > 0) { offset--; } ITypedRegion prevRegion = context.getDocument().getPartition(offset); prevTextRegion = sdRegion.getRegionAtCharacterOffset(prevRegion.getOffset()); } catch(BadLocationException e) { //this should never happen Logger.logException(e); } // suggest JSP Expression inside of XML comments if (completionRegion.getType() == DOMRegionContext.XML_COMMENT_TEXT && !isXMLFormat(doc)) { if (request == null) { request = new ContentAssistRequest(treeNode, xmlnode, sdRegion, completionRegion, documentPosition, 0, ""); //$NON-NLS-1$ } request.addProposal(new CustomCompletionProposal("<%= %>",//$NON-NLS-1$ documentPosition, 0, 4, JSPEditorPluginImageHelper.getInstance().getImage(JSPEditorPluginImages.IMG_OBJ_TAG_JSP), "jsp:expression", null, "<%= %>", XMLRelevanceConstants.R_JSP)); //$NON-NLS-1$ //$NON-NLS-2$ } /* handle proposals in and around JSP_DIRECTIVE_OPEN, * JSP_DIRECTIVE_CLOSE (preceded by OPEN) and JSP_DIRECTIVE_NAME */ else if ((completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN && documentPosition >= sdRegion.getTextEndOffset(completionRegion)) || (completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME && documentPosition <= sdRegion.getTextEndOffset(completionRegion)) || (completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE && prevTextRegion != null && prevTextRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN && documentPosition <= sdRegion.getTextEndOffset(completionRegion))) { if (completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_OPEN || completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE) { if (request == null) { request = new ContentAssistRequest(xmlnode, xmlnode, sdRegion, completionRegion, documentPosition, 0, matchString); } //determine if there is any part of a directive name already existing Iterator regions = sdRegion.getRegions().iterator(); String nameString = null; int begin = request.getReplacementBeginPosition(); int length = request.getReplacementLength(); while (regions.hasNext()) { ITextRegion region = (ITextRegion) regions.next(); if (region.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME) { nameString = sdRegion.getText(region); begin = sdRegion.getStartOffset(region); length = region.getTextLength(); break; } } if (nameString == null) { nameString = ""; //$NON-NLS-1$ } /* Suggest the directive names that have been determined to be * appropriate based on existing content */ for (int i = 0; i < directiveNames.length; i++) { if (directiveNames[i].startsWith(nameString) || documentPosition <= begin) { request.addProposal(new CustomCompletionProposal(directiveNames[i], begin, length, directiveNames[i].length(), JSPEditorPluginImageHelper.getInstance().getImage(JSPEditorPluginImages.IMG_OBJ_TAG_JSP), directiveNames[i], null, null, XMLRelevanceConstants.R_JSP)); } } } // by default, JSP_DIRECTIVE_NAME else { if (request == null) { request = new ContentAssistRequest(xmlnode, xmlnode, sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); } //add each of the directive names as a proposal for (int i = 0; i < directiveNames.length; i++) { if (directiveNames[i].startsWith(matchString)) { request.addProposal(new CustomCompletionProposal( directiveNames[i], request.getReplacementBeginPosition(), request.getReplacementLength(), directiveNames[i].length(), JSPEditorPluginImageHelper.getInstance().getImage(JSPEditorPluginImages.IMG_OBJ_TAG_JSP), directiveNames[i], null, null, XMLRelevanceConstants.R_JSP)); } } } } else if ((completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME && documentPosition > sdRegion.getTextEndOffset(completionRegion)) || (completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE && documentPosition <= sdRegion.getStartOffset(completionRegion))) { if (request == null) { request = computeAttributeProposals(matchString, completionRegion, treeNode, xmlnode, context); } } // no name?: <%@ %> else if (completionRegion.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_CLOSE && documentPosition <= sdRegion.getStartOffset(completionRegion)) { if (request != null) { request = computeAttributeProposals(matchString, completionRegion, treeNode, xmlnode, context); } Iterator regions = sdRegion.getRegions().iterator(); String nameString = null; while (regions.hasNext()) { ITextRegion region = (ITextRegion) regions.next(); if (region.getType() == DOMJSPRegionContexts.JSP_DIRECTIVE_NAME) { nameString = sdRegion.getText(region); break; } } if (nameString == null) { for (int i = 0; i < directiveNames.length; i++) { request.addProposal(new CustomCompletionProposal( directiveNames[i], request.getReplacementBeginPosition(), request.getReplacementLength(), directiveNames[i].length(), JSPEditorPluginImageHelper.getInstance().getImage(JSPEditorPluginImages.IMG_OBJ_TAG_JSP), directiveNames[i], null, null, XMLRelevanceConstants.R_JSP)); } } } return request; } /** * @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) { //nothing to suggest here } /** * @param doc determine if this doc is XML format * @return <code>true</code> if the given doc is of XML format, <code>false</code> otherwise. */ private boolean isXMLFormat(Document doc) { if (doc == null) return false; Element docElement = doc.getDocumentElement(); return docElement != null && ((docElement.getNodeName().equals("jsp:root")) || //$NON-NLS-1$ ((((IDOMNode) docElement).getStartStructuredDocumentRegion() == null && ((IDOMNode) docElement).getEndStructuredDocumentRegion() == null))) || declaresNameSpacesInRoot(docElement); } private boolean declaresNameSpacesInRoot(Element docElement) { if (docElement.hasAttribute("xmlns:jsp")) return true; NamedNodeMap attributes = docElement.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Attr attr = (Attr) attributes.item(i); String prefix = attr.getPrefix(); if (prefix != null && "xmlns".equals(prefix)) { return true; } } return false; } }