diff options
Diffstat (limited to 'bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLContentAssistUtilities.java')
-rw-r--r-- | bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLContentAssistUtilities.java | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLContentAssistUtilities.java b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLContentAssistUtilities.java new file mode 100644 index 0000000000..a6b86f49f4 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.ui/src/org/eclipse/wst/xml/ui/internal/contentassist/XMLContentAssistUtilities.java @@ -0,0 +1,492 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * Jens Lukowski/Innoopract - initial renaming/restructuring + * + *******************************************************************************/ +package org.eclipse.wst.xml.ui.internal.contentassist; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.osgi.util.NLS; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.internal.util.ScriptLanguageKeys; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils; +import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal; +import org.eclipse.wst.xml.core.document.IDOMElement; +import org.eclipse.wst.xml.core.document.IDOMNode; +import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext; +import org.eclipse.wst.xml.ui.internal.XMLUIMessages; +import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImageHelper; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +/** + * @author pavery + */ +public class XMLContentAssistUtilities extends ContentAssistUtils { + + /** + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on "nestedContext". + */ + private class DOMJSPRegionContextsPrivateCopy { + private static final String JSP_CLOSE = "JSP_CLOSE"; //$NON-NLS-1$ + private static final String JSP_DECLARATION_OPEN = "JSP_DECLARATION_OPEN"; //$NON-NLS-1$ + private static final String JSP_SCRIPTLET_OPEN = "JSP_SCRIPTLET_OPEN"; //$NON-NLS-1$ + private static final String JSP_EXPRESSION_OPEN = "JSP_EXPRESSION_OPEN"; //$NON-NLS-1$ + + } + + + public static final String CONTENT = "Content"; //$NON-NLS-1$ + public static final String CONTENT_SCRIPT_TYPE = "Content-Script-Type"; //$NON-NLS-1$ + public static final String HEAD = "HEAD"; //$NON-NLS-1$ + public static final String HTML = "HTML"; //$NON-NLS-1$ + public static final String HTTP_EQUIV = "HTTP-EQUIV"; //$NON-NLS-1$ + public static final String META = "META"; //$NON-NLS-1$ + + /** + * A convenience method for getting the closing proposal given the + * contents (IndexedRegion) of a tag that is started, but possibly not + * ended + * + * @param viewer + * the text viewer + * @param documentPosition + * the cursor position in the viewer + * @param indexedNode + * the contents of the tag that is started but possibly not + * ended + * @param parentTagName + * the tag on which you are checkin for an ending tag + * @param imagePath + * content assist image path in realation to com.ibm.sed. + * structured. contentassist. xmlSourceEditorImageHelper + * @return ICompletionProposal + */ + public static ICompletionProposal computeJSPEndTagProposal(ITextViewer viewer, int documentPosition, IndexedRegion indexedNode, String parentTagName, String imagePath) { + ICompletionProposal p = null; + + // check if tag is closed + boolean hasEndTag = true; + boolean isJSPTag = false; + IDOMNode xnode = null; + String tagName = ""; //$NON-NLS-1$ + if (indexedNode instanceof IDOMNode) { + xnode = ((IDOMNode) indexedNode); + // it's ended already... + if (xnode.getEndStructuredDocumentRegion() != null) + return null; + IDOMNode openNode = null; + if (!xnode.getNodeName().equalsIgnoreCase(parentTagName)) + openNode = (IDOMNode) xnode.getParentNode(); + if (openNode != null) { + if (openNode instanceof IDOMElement) { + isJSPTag = ((IDOMElement) openNode).isJSPTag(); + } + tagName = openNode.getNodeName(); + hasEndTag = (openNode.getEndStructuredDocumentRegion() != null); + } + } + + // it's closed, don't add close tag proposal + if (!hasEndTag && !isJSPTag) { + + // create appropriate close tag text + String proposedText = proposedText = "</" + tagName; //$NON-NLS-1$ + String viewerText = viewer.getTextWidget().getText(); + if (viewerText.length() >= documentPosition && viewerText.length() >= 2 && documentPosition >= 2) { + String last2chars = viewerText.substring(documentPosition - 2, documentPosition); + if (last2chars.endsWith("</")) //$NON-NLS-1$ + proposedText = tagName; + else if (last2chars.endsWith("<")) //$NON-NLS-1$ + proposedText = "/" + tagName; //$NON-NLS-1$ + } + + // create proposal + p = new CustomCompletionProposal(proposedText + ">", //$NON-NLS-1$ + documentPosition, 0, proposedText.length() + 1, XMLEditorPluginImageHelper.getInstance().getImage(imagePath), //$NON-NLS-1$ + NLS.bind(XMLUIMessages.End_with_, (new Object[]{proposedText})), + null, null, XMLRelevanceConstants.R_END_TAG); + } + else if (!hasEndTag && isJSPTag) { + + // create appropriate close tag text + String proposedText = proposedText = "%"; //$NON-NLS-1$ + String viewerText = viewer.getTextWidget().getText(); + + // TODO (pa) make it smarter to add "%>" or just ">" if % is + // already there... + if (viewerText.length() >= documentPosition && viewerText.length() >= 2) { + String last2chars = viewerText.substring(documentPosition - 2, documentPosition); + String lastchar = viewerText.substring(documentPosition - 1, documentPosition); + if (lastchar.equals("%")) //$NON-NLS-1$ + { + if (last2chars.endsWith("<%")) //$NON-NLS-1$ + proposedText = "%"; //$NON-NLS-1$ + else + proposedText = ""; //$NON-NLS-1$ + } + } + + // create proposal + p = new CustomCompletionProposal(proposedText + ">", //$NON-NLS-1$ + documentPosition, 0, proposedText.length() + 1, XMLEditorPluginImageHelper.getInstance().getImage(imagePath), //$NON-NLS-1$ + NLS.bind(XMLUIMessages.End_with_, (new Object[]{proposedText})), + null, null, XMLRelevanceConstants.R_END_TAG); + } + + return p; + } + + + /** + * A convenience method for getting the closing proposal given the + * contents (IndexedRegion) of a tag that is started, but possibly not + * ended + * + * @param viewer + * the text viewer + * @param documentPosition + * the cursor position in the viewer + * @param indexedNode + * the contents of the tag that is started but possibly not + * ended + * @param parentTagName + * the tag on which you are checkin for an ending tag + * @param imagePath + * content assist image path in realation to com.ibm.sed. + * structured. contentassist. xmlSourceEditorImageHelper + * @return ICompletionProposal + */ + public static ICompletionProposal computeXMLEndTagProposal(ITextViewer viewer, int documentPosition, IndexedRegion indexedNode, String parentTagName, String imagePath) { + ICompletionProposal p = null; + + // check if tag is closed + boolean hasEndTag = true; + IDOMNode xnode = null; + String tagName = ""; //$NON-NLS-1$ + if (indexedNode instanceof IDOMNode) { + xnode = ((IDOMNode) indexedNode); + // it's ended already... + if (xnode.getEndStructuredDocumentRegion() != null) + return null; + IDOMNode styleNode = null; + if (!xnode.getNodeName().equalsIgnoreCase(parentTagName)) + styleNode = (IDOMNode) xnode.getParentNode(); + if (styleNode != null) { + tagName = styleNode.getNodeName(); + hasEndTag = (styleNode.getEndStructuredDocumentRegion() != null); + } + } + + // it's closed, don't add close tag proposal + if (!hasEndTag) { + + // create appropriate close tag text + String proposedText = proposedText = "</" + tagName; //$NON-NLS-1$ + String viewerText = viewer.getTextWidget().getText(); + if (viewerText.length() >= documentPosition && viewerText.length() >= 2 && documentPosition >= 2) { + String last2chars = viewerText.substring(documentPosition - 2, documentPosition); + if (last2chars.endsWith("</")) //$NON-NLS-1$ + proposedText = tagName; + else if (last2chars.endsWith("<")) //$NON-NLS-1$ + proposedText = "/" + tagName; //$NON-NLS-1$ + } + + // create proposal + p = new CustomCompletionProposal(proposedText + ">", //$NON-NLS-1$ + documentPosition, 0, proposedText.length() + 1, XMLEditorPluginImageHelper.getInstance().getImage(imagePath), //$NON-NLS-1$ + NLS.bind(XMLUIMessages.End_with_, (new Object[]{proposedText})), + null, null, XMLRelevanceConstants.R_END_TAG); + } + return p; + } + + private static String getMetaScriptType(Document doc) { + // Can not just do a Document.getElementsByTagName(String) as this + // needs + // to be relatively fast. + List metas = new ArrayList(); + // check for META tags under the Document + Node html = null; + Node head = null; + Node child = null; + // ---------------------------------------------------------------------- + // (pa) 20021217 + // cmvc defect 235554 + // performance enhancement: using child.getNextSibling() rather than + // nodeList(item) for O(n) vs. O(n*n) + // ---------------------------------------------------------------------- + + for (child = doc.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() != Node.ELEMENT_NODE) + continue; + if (child.getNodeName().equalsIgnoreCase(META)) + metas.add(child); + else if (child.getNodeName().equalsIgnoreCase(HTML)) + html = child; + } + // NodeList children = doc.getChildNodes(); + // for(int i = 0; i < children.getLength(); i++) { + // child = children.item(i); + // if(child.getNodeType() != Node.ELEMENT_NODE) + // continue; + // if(child.getNodeName().equalsIgnoreCase(META)) + // metas.add(child); + // else if(child.getNodeName().equalsIgnoreCase(HTML)) + // html = child; + // } + + // check for META tags under HEAD + if (html != null) { + for (child = html.getFirstChild(); child != null && head == null; child = child.getNextSibling()) { + if (child.getNodeType() != Node.ELEMENT_NODE) + continue; + if (child.getNodeName().equalsIgnoreCase(HEAD)) + head = child; + } + // children = html.getChildNodes(); + // for(int i = 0; i < children.getLength() && head == null; i++) { + // child = children.item(i); + // if(child.getNodeType() != Node.ELEMENT_NODE) + // continue; + // if(child.getNodeName().equalsIgnoreCase(HEAD)) + // head = child; + // } + } + + if (head != null) { + for (head.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() != Node.ELEMENT_NODE) + continue; + if (child.getNodeName().equalsIgnoreCase(META)) + metas.add(child); + } + // children = head.getChildNodes(); + // for(int i = 0 ; i < children.getLength(); i++) { + // child = children.item(i); + // if(child.getNodeType() != Node.ELEMENT_NODE) + // continue; + // if(child.getNodeName().equalsIgnoreCase(META)) + // metas.add(child); + // } + } + + return getMetaScriptType(metas); + } + + private static String getMetaScriptType(List metaNodeList) { + Node meta = null; + NamedNodeMap attributes = null; + boolean httpEquiv = false; + String contentScriptType = null; + + for (int i = metaNodeList.size() - 1; i >= 0; i--) { + meta = (Node) metaNodeList.get(i); + attributes = meta.getAttributes(); + httpEquiv = false; + contentScriptType = null; + for (int j = 0; j < attributes.getLength(); j++) { + if (attributes.item(j).getNodeName().equalsIgnoreCase(HTTP_EQUIV)) { + httpEquiv = attributes.item(j).getNodeValue().equalsIgnoreCase(CONTENT_SCRIPT_TYPE); + } + else if (attributes.item(j).getNodeName().equalsIgnoreCase(CONTENT)) { + contentScriptType = attributes.item(j).getNodeValue(); + } + } + if (httpEquiv && contentScriptType != null) + return contentScriptType; + } + return null; + } + + /** + * Returns the scripting language the scriptNode is in Currently returns + * javascript unless some unknown type or language is specified. Then the + * unknown type/language is returned + * + * @param scriptNode + */ + public static String getScriptLanguage(Node scriptNode) { + Node attr = null; + + boolean specified = false; + // try to find a scripting adapter for 'type' + if ((scriptNode == null) || (scriptNode.getAttributes() == null)) + return null; + + attr = scriptNode.getAttributes().getNamedItem("type");//$NON-NLS-1$ + if (attr != null) { + specified = true; + String type = attr.getNodeValue(); + return lookupScriptType(type); + } + // now try to find a scripting adapter for 'language' (deprecated by + // HTML specifications) + attr = scriptNode.getAttributes().getNamedItem("language");//$NON-NLS-1$ + if (attr != null) { + specified = true; + String language = attr.getNodeValue(); + return lookupScriptLanguage(language); + } + // check if one is specified by a META tag at the root level or inside + // of HEAD + String type = null; + if (!specified) + type = getMetaScriptType(scriptNode.getOwnerDocument()); + if (type != null) { + specified = true; + return lookupScriptType(type); + } + // return default + if (!specified) + return ScriptLanguageKeys.JAVASCRIPT; + return null; + } + + /** + * Tells you if the flatnode is the %> delimiter + * + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on + * "nestedContext". + */ + public static boolean isJSPCloseDelimiter(IStructuredDocumentRegion fn) { + if (fn == null) + return false; + return isJSPCloseDelimiter(fn.getType()); + } + + /** + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on + * "nestedContext". + */ + public static boolean isJSPCloseDelimiter(String type) { + if (type == null) + return false; + return (type.equals(DOMJSPRegionContextsPrivateCopy.JSP_CLOSE) || type.equals(DOMRegionContext.XML_TAG_CLOSE)); + } + + /** + * Tells you if the flatnode is the JSP region <%%>, <%=%>, <%!%> + * + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on + * "nestedContext". + */ + public static boolean isJSPDelimiter(IStructuredDocumentRegion fn) { + boolean isDelimiter = false; + String type = fn.getType(); + if (type != null) { + isDelimiter = isJSPDelimiter(type); + } + return isDelimiter; + } + + /** + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on + * "nestedContext". + */ + public static boolean isJSPDelimiter(String type) { + if (type == null) + return false; + return (isJSPOpenDelimiter(type) || isJSPCloseDelimiter(type)); + } + + /** + * Tells you if the flatnode is <%, <%=, or <%! ISSUE: this is a bit of + * hidden JSP knowledge that was implemented this way for expedency. + * Should be evolved in future to depend on "nestedContext". + */ + public static boolean isJSPOpenDelimiter(IStructuredDocumentRegion fn) { + if (fn == null) + return false; + return isJSPOpenDelimiter(fn.getType()); + } + + /** + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on + * "nestedContext". + */ + public static boolean isJSPOpenDelimiter(String type) { + if (type == null) + return false; + return (type.equals(DOMJSPRegionContextsPrivateCopy.JSP_SCRIPTLET_OPEN) || type.equals(DOMJSPRegionContextsPrivateCopy.JSP_DECLARATION_OPEN) || type.equals(DOMJSPRegionContextsPrivateCopy.JSP_EXPRESSION_OPEN)); + } + + /** + * Tells you if the flatnode is the <jsp:scriptlet>, <jsp:expression>, or + * <jsp:declaration>tag + * + * ISSUE: this is a bit of hidden JSP knowledge that was implemented this + * way for expedency. Should be evolved in future to depend on + * "nestedContext". + */ + public static boolean isXMLJSPDelimiter(IStructuredDocumentRegion fn) { + boolean isDelimiter = false; + if (fn != null && fn instanceof ITextRegionContainer) { + Object[] regions = ((ITextRegionContainer) fn).getRegions().toArray(); + ITextRegion temp = null; + String regionText = ""; //$NON-NLS-1$ + for (int i = 0; i < regions.length; i++) { + temp = (ITextRegion) regions[i]; + if (temp.getType() == DOMRegionContext.XML_TAG_NAME) { + regionText = fn.getText(temp); + if (regionText.equalsIgnoreCase("jsp:scriptlet") || regionText.equalsIgnoreCase("jsp:expression") || regionText.equalsIgnoreCase("jsp:declaration")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + isDelimiter = true; + } + } + } + return isDelimiter; + } + + /** + * Returns "javascript" if language attribute is some form of javascript, + * "java" if language attribute is some form of java. Otherwise, just + * returns type. + * + * @param language + */ + public static String lookupScriptLanguage(String language) { + for (int i = 0; i < ScriptLanguageKeys.JAVASCRIPT_LANGUAGE_KEYS.length; i++) { + if (ScriptLanguageKeys.JAVASCRIPT_LANGUAGE_KEYS[i].equalsIgnoreCase(language)) + return ScriptLanguageKeys.JAVASCRIPT; + } + for (int i = 0; i < ScriptLanguageKeys.JAVA_LANGUAGE_KEYS.length; i++) { + if (ScriptLanguageKeys.JAVA_LANGUAGE_KEYS[i].equalsIgnoreCase(language)) + return ScriptLanguageKeys.JAVA; + } + return language; + } + + /** + * Returns "javascript" if type (used in <script type="xxx"> is actually + * javascript type. Otherwise, just returns type + * + * @param type + */ + public static String lookupScriptType(String type) { + for (int i = 0; i < ScriptLanguageKeys.JAVASCRIPT_MIME_TYPE_KEYS.length; i++) + if (ScriptLanguageKeys.JAVASCRIPT_MIME_TYPE_KEYS[i].equalsIgnoreCase(type)) + return ScriptLanguageKeys.JAVASCRIPT; + return type; + } +} |