| /******************************************************************************* |
| * 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.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Vector; |
| |
| 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.osgi.util.NLS; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.wst.sse.core.StructuredModelManager; |
| import org.eclipse.wst.sse.core.internal.encoding.ContentTypeEncodingPreferences; |
| import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| 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.internal.util.Debug; |
| import org.eclipse.wst.sse.ui.internal.IReleasable; |
| 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.contentmodel.CMAttributeDeclaration; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMContent; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType; |
| 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.contentmodel.CMEntityDeclaration; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMGroup; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; |
| import org.eclipse.wst.xml.core.internal.contentmodel.CMNodeList; |
| import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; |
| import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQueryAction; |
| import org.eclipse.wst.xml.core.internal.contentmodel.util.DOMNamespaceHelper; |
| import org.eclipse.wst.xml.core.internal.document.AttrImpl; |
| import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; |
| import org.eclipse.wst.xml.core.internal.provisional.contenttype.ContentTypeIdForXML; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; |
| 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.Logger; |
| import org.eclipse.wst.xml.ui.internal.XMLUIMessages; |
| import org.eclipse.wst.xml.ui.internal.XMLUIPlugin; |
| import org.eclipse.wst.xml.ui.internal.editor.CMImageUtil; |
| import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImageHelper; |
| import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImages; |
| import org.eclipse.wst.xml.ui.internal.preferences.XMLUIPreferenceNames; |
| import org.eclipse.wst.xml.ui.internal.taginfo.MarkupTagInfoProvider; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.DocumentType; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| abstract public class AbstractContentAssistProcessor implements IContentAssistProcessor, IReleasable { |
| /** |
| * 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_COMMENT_CLOSE = "JSP_COMMENT_CLOSE"; //$NON-NLS-1$ |
| |
| private static final String JSP_COMMENT_OPEN = "JSP_COMMENT_OPEN"; //$NON-NLS-1$ |
| private static final String JSP_DECLARATION_OPEN = "JSP_DECLARATION_OPEN"; //$NON-NLS-1$ |
| private static final String JSP_DIRECTIVE_CLOSE = "JSP_DIRECTIVE_CLOSE"; //$NON-NLS-1$ |
| private static final String JSP_DIRECTIVE_NAME = "JSP_DIRECTIVE_NAME"; //$NON-NLS-1$ |
| |
| private static final String JSP_DIRECTIVE_OPEN = "JSP_DIRECTIVE_OPEN"; //$NON-NLS-1$ |
| private static final String JSP_EXPRESSION_OPEN = "JSP_EXPRESSION_OPEN"; //$NON-NLS-1$ |
| |
| private static final String JSP_ROOT_TAG_NAME = "JSP_ROOT_TAG_NAME"; //$NON-NLS-1$ |
| |
| private static final String JSP_SCRIPTLET_OPEN = "JSP_SCRIPTLET_OPEN"; //$NON-NLS-1$ |
| |
| } |
| |
| protected static final String INTERNALERROR = XMLUIMessages.SEVERE_internal_error_occu_UI_; |
| protected static final String UNKNOWN_ATTR = XMLUIMessages.No_known_attribute__UI_; |
| protected static final String UNKNOWN_CONTEXT = XMLUIMessages.Content_Assist_not_availab_UI_; |
| protected char completionProposalAutoActivationCharacters[] = null; |
| protected char contextInformationAutoActivationCharacters[] = null; |
| private AttributeContextInformationProvider fAttributeInfoProvider = null; |
| private AttributeContextInformationPresenter fContextInformationPresenter = null; |
| |
| protected String fErrorMessage = null; |
| protected XMLContentModelGenerator fGenerator; |
| // protected IResource resource = null; |
| protected MarkupTagInfoProvider fInfoProvider = null; |
| protected ITextViewer fTextViewer = null; |
| |
| private final boolean showValues = true; |
| |
| public AbstractContentAssistProcessor() { |
| init(); |
| } |
| |
| protected void addAttributeNameProposals(ContentAssistRequest contentAssistRequest) { |
| IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); |
| IStructuredDocumentRegion sdRegion = contentAssistRequest.getDocumentRegion(); |
| // retrieve the list of attributes |
| CMElementDeclaration elementDecl = getCMElementDeclaration(node); |
| if (elementDecl != null) { |
| CMNamedNodeMap attributes = elementDecl.getAttributes(); |
| String matchString = contentAssistRequest.getMatchString(); |
| |
| // check whether an attribute really exists for the replacement |
| // offsets AND if it possesses a value |
| boolean attrAtLocationHasValue = false; |
| NamedNodeMap attrs = node.getAttributes(); |
| for (int i = 0; i < attrs.getLength(); i++) { |
| AttrImpl existingAttr = (AttrImpl) attrs.item(i); |
| ITextRegion name = existingAttr.getNameRegion(); |
| |
| if (sdRegion.getStartOffset(name) <= contentAssistRequest.getReplacementBeginPosition() && sdRegion.getStartOffset(name) + name.getLength() >= contentAssistRequest.getReplacementBeginPosition() + contentAssistRequest.getReplacementLength() && existingAttr.getValueRegion() != null) { |
| attrAtLocationHasValue = true; |
| break; |
| } |
| } |
| |
| // only add proposals for the attributes whose names begin with |
| // the matchstring |
| if (attributes != null) { |
| for (int i = 0; i < attributes.getLength(); i++) { |
| CMAttributeDeclaration attrDecl = (CMAttributeDeclaration) attributes.item(i); |
| |
| int isRequired = 0; |
| if (attrDecl.getUsage() == CMAttributeDeclaration.REQUIRED) { |
| isRequired = XMLRelevanceConstants.R_REQUIRED; |
| } |
| |
| boolean showAttribute = true; |
| showAttribute = showAttribute && beginsWith(getRequiredName(node, attrDecl), matchString.trim()); |
| AttrImpl attr = (AttrImpl) node.getAttributes().getNamedItem(getRequiredName(node, attrDecl)); |
| ITextRegion nameRegion = attr != null ? attr.getNameRegion() : null; |
| // nameRegion.getEndOffset() + 1 is required to allow for |
| // matches against the full name of an existing Attr |
| showAttribute = showAttribute && ((attr == null) || (nameRegion != null && sdRegion.getStartOffset(nameRegion) <= contentAssistRequest.getReplacementBeginPosition() && sdRegion.getStartOffset(nameRegion) + nameRegion.getLength() >= contentAssistRequest.getReplacementBeginPosition() + contentAssistRequest.getReplacementLength())); |
| if (showAttribute) { |
| Image attrImage = CMImageUtil.getImage(attrDecl); |
| if (attrImage == null) { |
| if (isRequired > 0) { |
| attrImage = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATT_REQ_OBJ); |
| } |
| else { |
| attrImage = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE); |
| } |
| } |
| |
| String proposedText = null; |
| String proposedInfo = getAdditionalInfo(elementDecl, attrDecl); |
| CustomCompletionProposal proposal = null; |
| // attribute is at this location and already exists |
| if (attrAtLocationHasValue) { |
| // only propose the name |
| proposedText = getRequiredName(node, attrDecl); |
| proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), proposedText.length(), attrImage, proposedText, null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_NAME + isRequired, true); |
| } |
| // no attribute exists or is elsewhere, generate |
| // minimally |
| else { |
| Attr existingAttrNode = (Attr) node.getAttributes().getNamedItem(getRequiredName(node, attrDecl)); |
| String value = null; |
| if (existingAttrNode != null) |
| value = existingAttrNode.getNodeValue(); |
| if (value != null && value.length() > 0) |
| proposedText = getRequiredName(node, attrDecl); |
| else { |
| proposedText = getRequiredText(node, attrDecl); |
| } |
| proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), attrDecl.getNodeName().length() + 2, attrImage, |
| // if the value isn't empty (no empty set of |
| // quotes), show it |
| (showValues && proposedText.indexOf("\"\"") < 0) ? proposedText : getRequiredName(node, attrDecl), //$NON-NLS-1$ |
| null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_NAME + isRequired); |
| } |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| } |
| else { |
| setErrorMessage(NLS.bind(XMLUIMessages.Element__is_unknown, (new Object[]{node.getNodeName()}))); |
| } |
| } |
| |
| protected void addAttributeValueProposals(ContentAssistRequest contentAssistRequest) { |
| |
| IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); |
| |
| // Find the attribute region and name for which this position should |
| // have a value proposed |
| IStructuredDocumentRegion open = node.getFirstStructuredDocumentRegion(); |
| ITextRegionList openRegions = open.getRegions(); |
| int i = openRegions.indexOf(contentAssistRequest.getRegion()); |
| if (i < 0) |
| return; |
| ITextRegion nameRegion = null; |
| while (i >= 0) { |
| nameRegion = openRegions.get(i--); |
| if (nameRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) |
| break; |
| } |
| |
| // the name region is REQUIRED to do anything useful |
| if (nameRegion != null) { |
| // Retrieve the declaration |
| CMElementDeclaration elementDecl = getCMElementDeclaration(node); |
| |
| // String attributeName = nameRegion.getText(); |
| String attributeName = open.getText(nameRegion); |
| |
| CMAttributeDeclaration attrDecl = null; |
| |
| // No CMElementDeclaration means no attribute metadata, but |
| // retrieve the |
| // declaration for the attribute otherwise |
| if (elementDecl != null) { |
| CMNamedNodeMap attributes = elementDecl.getAttributes(); |
| String noprefixName = DOMNamespaceHelper.getUnprefixedName(attributeName); |
| if (attributes != null) { |
| attrDecl = (CMAttributeDeclaration) attributes.getNamedItem(noprefixName); |
| if (attrDecl == null) { |
| attrDecl = (CMAttributeDeclaration) attributes.getNamedItem(attributeName); |
| } |
| } |
| if (attrDecl == null) { |
| setErrorMessage(UNKNOWN_ATTR, attributeName); |
| } |
| } |
| |
| String currentValue = node.getAttributes().getNamedItem(attributeName).getNodeValue(); |
| String proposedInfo = null; |
| Image image = CMImageUtil.getImage(attrDecl); |
| if (image == null) { |
| if (attrDecl != null && attrDecl.getUsage() == CMAttributeDeclaration.REQUIRED) { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATT_REQ_OBJ); |
| } |
| else { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE); |
| } |
| } |
| |
| if (attrDecl != null && attrDecl.getAttrType() != null) { |
| // attribute is known, prompt with values from the declaration |
| proposedInfo = getAdditionalInfo(elementDecl, attrDecl); |
| List possibleValues = getPossibleDataTypeValues(node, attrDecl); |
| if (possibleValues.size() > 0) { |
| // ENUMERATED VALUES |
| String matchString = contentAssistRequest.getMatchString(); |
| if (matchString == null) |
| matchString = ""; //$NON-NLS-1$ |
| if (matchString.length() > 0 && (matchString.startsWith("\"") || matchString.startsWith("'"))) //$NON-NLS-2$//$NON-NLS-1$ |
| matchString = matchString.substring(1); |
| boolean currentValid = false; |
| |
| // d210858, if the region's a container, don't suggest the |
| // enumerated values as they probably won't help |
| boolean existingComplicatedValue = contentAssistRequest.getRegion() != null && contentAssistRequest.getRegion() instanceof ITextRegionContainer; |
| if (!existingComplicatedValue) { |
| for (Iterator j = possibleValues.iterator(); j.hasNext();) { |
| String possibleValue = (String) j.next(); |
| currentValid = currentValid || possibleValue.equals(currentValue); |
| if (matchString.length() == 0 || possibleValue.startsWith(matchString)) { |
| |
| String rString = "\"" + possibleValue + "\""; //$NON-NLS-2$//$NON-NLS-1$ |
| int rOffset = contentAssistRequest.getReplacementBeginPosition(); |
| int rLength = contentAssistRequest.getReplacementLength(); |
| int cursorAfter = possibleValue.length() + 1; |
| String displayString = "\"" + possibleValue + "\""; //$NON-NLS-2$//$NON-NLS-1$ |
| |
| CustomCompletionProposal proposal = new CustomCompletionProposal(rString, rOffset, rLength, cursorAfter, image, displayString, null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| } |
| else if ((attrDecl.getUsage() == CMAttributeDeclaration.FIXED || attrDecl.getAttrType().getImpliedValueKind() == CMDataType.IMPLIED_VALUE_FIXED) && attrDecl.getAttrType().getImpliedValue() != null) { |
| // FIXED values |
| String value = attrDecl.getAttrType().getImpliedValue(); |
| if (value != null && value.length() > 0) { |
| String rValue = "\"" + value + "\"";//$NON-NLS-2$//$NON-NLS-1$ |
| CustomCompletionProposal proposal = new CustomCompletionProposal(rValue, |
| contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), rValue.length() + 1, image, rValue, null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); |
| contentAssistRequest.addProposal(proposal); |
| if (currentValue.length() > 0 && !value.equals(currentValue)) { |
| rValue = "\"" + currentValue + "\""; //$NON-NLS-2$//$NON-NLS-1$ |
| proposal = new CustomCompletionProposal(rValue, |
| contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), rValue.length() + 1, image, rValue, null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| } |
| else { |
| // unknown attribute, so supply nice empty values |
| proposedInfo = getAdditionalInfo(null, elementDecl); |
| CustomCompletionProposal proposal = null; |
| if (currentValue != null && currentValue.length() > 0) { |
| String rValue = "\"" + currentValue + "\""; //$NON-NLS-2$//$NON-NLS-1$ |
| proposal = new CustomCompletionProposal(rValue, |
| contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), 1, image, rValue, //$NON-NLS-2$//$NON-NLS-1$ |
| null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| else |
| setErrorMessage(UNKNOWN_CONTEXT); |
| } |
| |
| protected void addCommentProposal(ContentAssistRequest contentAssistRequest) { |
| contentAssistRequest.addProposal(new CustomCompletionProposal("<!-- -->", //$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), 5, XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_COMMENT), NLS.bind(XMLUIMessages.Comment__, (new Object[]{" <!-- -->"})), //$NON-NLS-1$ |
| null, null, XMLRelevanceConstants.R_COMMENT)); |
| } |
| |
| /** |
| * Add all of the element declarations int the CMContent object into one |
| * big list. |
| */ |
| protected void addContent(List contentList, CMContent content) { |
| if (content == null) |
| return; |
| if (content instanceof CMGroup) { |
| CMNodeList children = ((CMGroup) content).getChildNodes(); |
| if (children == null) |
| return; |
| for (int i = 0; i < children.getLength(); i++) { |
| CMNode child = children.item(i); |
| if (child.getNodeType() == CMNode.ELEMENT_DECLARATION) { |
| contentList.add(child); |
| } |
| else { |
| if (child.getNodeType() == CMNode.GROUP) { |
| addContent(contentList, (CMContent) child); |
| } |
| else { |
| throw new IllegalArgumentException("Unknown content child: " + child); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| else { |
| contentList.add(content); |
| } |
| } |
| |
| protected void addDocTypeProposal(ContentAssistRequest contentAssistRequest) { |
| // if a DocumentElement exists, use that for the root Element name |
| String rootname = "unspecified"; //$NON-NLS-1$ |
| if (contentAssistRequest.getNode().getOwnerDocument().getDocumentElement() != null) |
| rootname = contentAssistRequest.getNode().getOwnerDocument().getDocumentElement().getNodeName(); |
| |
| String proposedText = "<!DOCTYPE " + rootname + " PUBLIC \"//UNKNOWN/\" \"unknown.dtd\">"; //$NON-NLS-1$ //$NON-NLS-2$ |
| ICompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), 10, XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_DOCTYPE), "<!DOCTYPE ... >", //$NON-NLS-1$ |
| null, null, XMLRelevanceConstants.R_DOCTYPE); |
| // TODO provide special documentation on doc type |
| contentAssistRequest.addProposal(proposal); |
| } |
| |
| /** |
| * Add the proposals for a completely empty document |
| */ |
| protected void addEmptyDocumentProposals(ContentAssistRequest contentAssistRequest) { |
| // nothing |
| } |
| |
| /** |
| * Add the proposals for the name in an end tag |
| */ |
| protected void addEndTagNameProposals(ContentAssistRequest contentAssistRequest) { |
| |
| if (contentAssistRequest.getStartOffset() + contentAssistRequest.getRegion().getTextLength() < contentAssistRequest.getReplacementBeginPosition()) { |
| CustomCompletionProposal proposal = new CustomCompletionProposal(">", //$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), 1, XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC), NLS.bind(XMLUIMessages.Close_with__, (new Object[]{" '>'"})), //$NON-NLS-1$ |
| null, null, XMLRelevanceConstants.R_END_TAG_NAME); |
| contentAssistRequest.addProposal(proposal); |
| } |
| else { |
| IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); |
| ModelQuery modelQuery = ModelQueryUtil.getModelQuery(node.getOwnerDocument()); |
| Node aNode = contentAssistRequest.getNode(); |
| String matchString = contentAssistRequest.getMatchString(); |
| if (matchString.startsWith("</")) //$NON-NLS-1$ |
| matchString = matchString.substring(2); |
| while (aNode != null) { |
| if (aNode.getNodeType() == Node.ELEMENT_NODE) { |
| if (aNode.getNodeName().startsWith(matchString)) { |
| IDOMNode aXMLNode = (IDOMNode) aNode; |
| CMElementDeclaration ed = modelQuery.getCMElementDeclaration((Element) aNode); |
| if ((aXMLNode.getEndStructuredDocumentRegion() == null) && (ed == null || (ed.getContentType() != CMElementDeclaration.EMPTY))) { |
| String proposedText = aNode.getNodeName(); |
| String proposedInfo = (ed != null) ? getAdditionalInfo(null, ed) : null; |
| if (node.getNodeType() == Node.TEXT_NODE && !contentAssistRequest.getDocumentRegion().isEnded()) |
| proposedText += ">"; //$NON-NLS-1$ |
| CustomCompletionProposal proposal = null; |
| // double check to see if the region acted upon is |
| // a tag name; replace it if so |
| Image image = CMImageUtil.getImage(ed); |
| if (image == null) { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| } |
| if (contentAssistRequest.getRegion().getType() == DOMRegionContext.XML_TAG_NAME) { |
| proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getStartOffset(), contentAssistRequest.getRegion().getTextLength(), proposedText.length(), image, proposedText, null, proposedInfo, XMLRelevanceConstants.R_END_TAG_NAME); |
| } |
| else { |
| proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), proposedText.length(), image, NLS.bind(XMLUIMessages.Close_with__, (new Object[]{"'" + proposedText + "'"})), //$NON-NLS-1$ //$NON-NLS-2$ |
| null, proposedInfo, XMLRelevanceConstants.R_END_TAG_NAME); |
| } |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| aNode = aNode.getParentNode(); |
| } |
| } |
| } |
| |
| /** |
| * Prompt for end tags to a non-empty Node that hasn't ended Handles these |
| * cases: <br> |
| * <tagOpen>| <br> |
| * <tagOpen>< |<br> |
| * <tagOpen></ | |
| * |
| * @param contentAssistRequest |
| */ |
| protected void addEndTagProposals(ContentAssistRequest contentAssistRequest) { |
| IDOMNode node = (IDOMNode) contentAssistRequest.getParent(); |
| |
| if (isCommentNode(node)) { |
| // loop and find non comment node parent |
| while (node != null && isCommentNode(node)) { |
| node = (IDOMNode) node.getParentNode(); |
| } |
| } |
| |
| // node is already closed |
| if(node.isClosed()) { |
| // loop and find non comment unclose node parent |
| while (node != null && node.isClosed()) { |
| node = (IDOMNode) node.getParentNode(); |
| } |
| } |
| // there were no unclosed tags |
| if(node == null) |
| return; |
| |
| // data to create a CustomCompletionProposal |
| String replaceText = node.getNodeName() + ">"; //$NON-NLS-1$ |
| int replaceBegin = contentAssistRequest.getReplacementBeginPosition(); |
| int replaceLength = contentAssistRequest.getReplacementLength(); |
| int cursorOffset = node.getNodeName().length() + 1; |
| String displayString = ""; //$NON-NLS-1$ |
| String proposedInfo = ""; //$NON-NLS-1$ |
| Image image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| |
| setErrorMessage(null); |
| boolean addProposal = false; |
| |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| // //////////////////////////////////////////////////////////////////////////////////// |
| IStructuredDocument sDoc = (IStructuredDocument) fTextViewer.getDocument(); |
| IStructuredDocumentRegion xmlEndTagOpen = sDoc.getRegionAtCharacterOffset(contentAssistRequest.getReplacementBeginPosition()); |
| // skip backward to "<", "</", or the (unclosed) start tag, null |
| // if not found |
| String type = ""; //$NON-NLS-1$ |
| while (xmlEndTagOpen != null && (type = xmlEndTagOpen.getType()) != DOMRegionContext.XML_END_TAG_OPEN && type != DOMRegionContext.XML_TAG_CLOSE && !needsEndTag(xmlEndTagOpen) && type != DOMRegionContext.XML_TAG_OPEN) { |
| xmlEndTagOpen = xmlEndTagOpen.getPrevious(); |
| } |
| |
| if (xmlEndTagOpen == null) |
| return; |
| |
| node = (IDOMNode) node.getModel().getIndexedRegion(xmlEndTagOpen.getStartOffset()); |
| node = (IDOMNode) node.getParentNode(); |
| |
| if (isStartTag(xmlEndTagOpen)) { |
| // this is the case for a start tag w/out end tag |
| // eg: |
| // <p> |
| // <% String test = "test"; %> |
| // | |
| if (needsEndTag(xmlEndTagOpen)) { |
| String tagName = getTagName(xmlEndTagOpen); |
| xmlEndTagOpen.getTextEndOffset(); |
| replaceLength = 0; |
| replaceText = "</" + tagName + ">"; //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$ |
| // replaceText = "</" + node.getNodeName() + ">"; |
| // //$NON-NLS-1$ $NON-NLS-2$ |
| cursorOffset = tagName.length() + 3; |
| displayString = NLS.bind(XMLUIMessages.End_with__, (new Object[]{tagName})); |
| addProposal = true; |
| } |
| } |
| else if (type == DOMRegionContext.XML_END_TAG_OPEN) { |
| // this is the case for: <tag> </ | |
| // possibly <tag> </ |<anotherTag> |
| // should only be replacing white space... |
| replaceLength = (replaceBegin > xmlEndTagOpen.getTextEndOffset()) ? replaceBegin - xmlEndTagOpen.getTextEndOffset() : 0; |
| replaceText = node.getNodeName() + ">"; //$NON-NLS-1$ |
| cursorOffset = replaceText.length(); |
| replaceBegin = xmlEndTagOpen.getTextEndOffset(); |
| displayString = NLS.bind(XMLUIMessages.End_with_, (new Object[]{node.getNodeName()})); |
| addProposal = true; |
| } |
| else if (type == DOMRegionContext.XML_TAG_OPEN) { |
| // this is the case for: <tag> < | |
| replaceText = "/" + node.getNodeName() + ">"; //$NON-NLS-1$ //$NON-NLS-2$ $NON-NLS-2$ |
| cursorOffset = replaceText.length(); |
| // replaceText = "/" + node.getNodeName() + ">"; //$NON-NLS-1$ |
| // $NON-NLS-2$ |
| // should only be replacing white space... |
| replaceLength = (replaceBegin > xmlEndTagOpen.getTextEndOffset()) ? replaceBegin - xmlEndTagOpen.getTextEndOffset() : 0; |
| replaceBegin = xmlEndTagOpen.getTextEndOffset(); |
| displayString = NLS.bind(XMLUIMessages.End_with_, (new Object[]{"/" + node.getNodeName()})); //$NON-NLS-1$ |
| addProposal = true; |
| } |
| } |
| // //////////////////////////////////////////////////////////////////////////////////// |
| // sometimes the node is not null, but |
| // getNodeValue() is null, put in a null check |
| else if (node.getNodeValue() != null && node.getNodeValue().indexOf("</") != -1) { //$NON-NLS-1$ |
| // the case where "</" is started, but the nodes comes in as a |
| // text node (instead of element) |
| // like this: <tag> </| |
| Node parent = node.getParentNode(); |
| if (parent != null && parent.getNodeType() != Node.DOCUMENT_NODE) { |
| replaceText = parent.getNodeName() + ">"; //$NON-NLS-1$ |
| cursorOffset = replaceText.length(); |
| displayString = NLS.bind(XMLUIMessages.End_with__, (new Object[]{parent.getNodeName()})); |
| setErrorMessage(null); |
| addProposal = true; |
| } |
| } |
| // //////////////////////////////////////////////////////////////////////////////////// |
| else if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| setErrorMessage(UNKNOWN_CONTEXT); |
| } |
| if (addProposal == true) { |
| CustomCompletionProposal proposal = new CustomCompletionProposal(replaceText, replaceBegin, replaceLength, cursorOffset, image, displayString, null, proposedInfo, XMLRelevanceConstants.R_END_TAG); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| |
| protected void addEntityProposals(ContentAssistRequest contentAssistRequest, int documentPosition, ITextRegion completionRegion, IDOMNode treeNode) { |
| ICompletionProposal[] eps = computeEntityReferenceProposals(documentPosition, completionRegion, treeNode); |
| for (int i = 0; eps != null && i < eps.length; i++) |
| contentAssistRequest.addProposal(eps[i]); |
| } |
| |
| protected void addEntityProposals(Vector proposals, Properties map, String key, int nodeOffset, IStructuredDocumentRegion sdRegion, ITextRegion completionRegion) { |
| if (map == null) |
| return; |
| String entityName = ""; //$NON-NLS-1$ |
| String entityValue = ""; //$NON-NLS-1$ |
| Image entityIcon = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ENTITY_REFERENCE); |
| String replacementText = ""; //$NON-NLS-1$ |
| String displayString = ""; //$NON-NLS-1$ |
| Enumeration keys = map.keys(); |
| |
| while (keys != null && keys.hasMoreElements()) { |
| entityName = (String) keys.nextElement(); |
| entityValue = map.getProperty(entityName); |
| // filter based on partial entity string... |
| if (entityName.toLowerCase().startsWith(key.toLowerCase()) || key.trim().equals("")) //$NON-NLS-1$ |
| { |
| // figure out selection...if text is selected, add it to |
| // selection length |
| int selectionLength = nodeOffset; |
| if (fTextViewer != null) { |
| selectionLength += fTextViewer.getSelectedRange().y; |
| } |
| // create a new proposal for entity string... |
| replacementText = "&" + entityName + ";"; //$NON-NLS-1$ //$NON-NLS-2$ |
| displayString = "&" + entityName + "; (" + entityValue + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| ICompletionProposal cp = new CustomCompletionProposal(replacementText, sdRegion.getStartOffset(completionRegion), selectionLength, replacementText.length(), entityIcon, displayString, null, null, XMLRelevanceConstants.R_ENTITY); |
| if (cp != null) { |
| proposals.add(cp); |
| } |
| } |
| } |
| } |
| |
| protected void addPCDATAProposal(String nodeName, ContentAssistRequest contentAssistRequest) { |
| CustomCompletionProposal proposal = new CustomCompletionProposal("<![CDATA[]]>", //$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), 9, XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_CDATASECTION), "CDATA Section", //$NON-NLS-1$ |
| null, null, XMLRelevanceConstants.R_CDATA); |
| contentAssistRequest.addProposal(proposal); |
| |
| proposal = new CustomCompletionProposal(nodeName, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), nodeName.length(), XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TXTEXT), "#PCDATA", //$NON-NLS-1$ |
| null, null, XMLRelevanceConstants.R_CDATA); |
| contentAssistRequest.addProposal(proposal); |
| } |
| |
| protected void addStartDocumentProposals(ContentAssistRequest contentAssistRequest) { |
| Node aNode = contentAssistRequest.getNode(); |
| boolean xmlpiFound = false; |
| Document owningDocument = aNode.getOwnerDocument(); |
| // ==> // int xmlpiNodePosition = -1; |
| |
| // make sure xmlpi is root element |
| // don't want doctype proposal if XMLPI isn't first element... |
| Node first = owningDocument.getFirstChild(); |
| boolean xmlpiIsFirstElement = (first != null && first.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE); |
| boolean insertDoctype = xmlpiIsFirstElement; |
| |
| for (Node child = owningDocument.getFirstChild(); child != null; child = child.getNextSibling()) { |
| boolean xmlpi = (child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE && child.getNodeName().equals("xml")); //$NON-NLS-1$ |
| xmlpiFound = xmlpiFound || xmlpi; |
| if (xmlpiFound) { |
| if (child instanceof IDOMNode) { |
| // ==> // int xmlpiNodePosition = |
| // ((XMLNode)child).getEndOffset(); |
| } |
| // skip white space and text |
| while ((child = child.getNextSibling()) != null && (child.getNodeType() == Node.TEXT_NODE)) { |
| // just skipping |
| } |
| // check if theres a node inbetween XMLPI and cursor position |
| if (child != null && child instanceof IDOMNode) { |
| if (contentAssistRequest.getReplacementBeginPosition() >= ((IDOMNode) child).getEndOffset() || !xmlpiIsFirstElement) { |
| insertDoctype = false; |
| } |
| } |
| break; |
| } |
| } |
| |
| if (xmlpiFound && owningDocument.getDoctype() == null && isCursorAfterXMLPI(contentAssistRequest) && insertDoctype) { |
| addDocTypeProposal(contentAssistRequest); |
| } |
| } |
| |
| /** |
| * Close an unclosed start tag |
| */ |
| protected void addTagCloseProposals(ContentAssistRequest contentAssistRequest) { |
| IDOMNode node = (IDOMNode) contentAssistRequest.getParent(); |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| |
| CMElementDeclaration elementDecl = getCMElementDeclaration(node); |
| String proposedInfo = (elementDecl != null) ? getAdditionalInfo(null, elementDecl) : null; |
| int contentType = (elementDecl != null) ? elementDecl.getContentType() : CMElementDeclaration.ANY; |
| // if it's XML and content doesn't HAVE to be element, add "/>" |
| // proposal. |
| boolean endWithSlashBracket = (getXML(node) && contentType != CMElementDeclaration.ELEMENT); |
| |
| Image image = CMImageUtil.getImage(elementDecl); |
| if (image == null) { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| } |
| |
| // is the start tag ended properly? |
| if (contentAssistRequest.getDocumentRegion() == node.getFirstStructuredDocumentRegion() && !(node.getFirstStructuredDocumentRegion()).isEnded()) { |
| setErrorMessage(null); |
| // Is this supposed to be an empty tag? Note that if we can't |
| // tell, we assume it's not. |
| if (elementDecl != null && elementDecl.getContentType() == CMElementDeclaration.EMPTY) { |
| // prompt with a self-closing end character if needed |
| CustomCompletionProposal proposal = new CustomCompletionProposal(getContentGenerator().getStartTagClose(node, elementDecl), contentAssistRequest.getReplacementBeginPosition(), |
| // this is one of the few times to ignore the length -- |
| // always insert |
| // contentAssistRequest.getReplacementLength(), |
| 0, getContentGenerator().getStartTagClose(node, elementDecl).length(), image, NLS.bind(XMLUIMessages.Close_with___, (new Object[]{getContentGenerator().getStartTagClose(node, elementDecl)})), |
| null, proposedInfo, XMLRelevanceConstants.R_CLOSE_TAG); |
| contentAssistRequest.addProposal(proposal); |
| } |
| else { |
| // prompt with a close for the start tag |
| CustomCompletionProposal proposal = new CustomCompletionProposal(">", //$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), |
| // this is one of the few times to ignore the |
| // length -- always insert |
| // contentAssistRequest.getReplacementLength(), |
| 0, 1, image, NLS.bind(XMLUIMessages.Close_with__, (new Object[]{" '>'"})), //$NON-NLS-1$ |
| null, proposedInfo, XMLRelevanceConstants.R_CLOSE_TAG); |
| contentAssistRequest.addProposal(proposal); |
| |
| // prompt with the closer for the start tag and an end tag |
| // if one is not present |
| if (node.getEndStructuredDocumentRegion() == null) { |
| // make sure tag name is actually what it thinks it |
| // is...(eg. <%@ vs. <jsp:directive) |
| IStructuredDocumentRegion sdr = contentAssistRequest.getDocumentRegion(); |
| String openingTagText = (sdr != null) ? sdr.getFullText() : ""; //$NON-NLS-1$ |
| if (openingTagText != null && openingTagText.indexOf(node.getNodeName()) != -1) { |
| proposal = new CustomCompletionProposal("></" + node.getNodeName() + ">", //$NON-NLS-2$//$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), |
| // this is one of the few times to |
| // ignore the length -- always insert |
| // contentAssistRequest.getReplacementLength(), |
| 0, 1, image, NLS.bind(XMLUIMessages.Close_with____, (new Object[]{node.getNodeName()})), |
| null, proposedInfo, XMLRelevanceConstants.R_CLOSE_TAG); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| // prompt with slash bracket "/>" incase if it's a self |
| // ending tag |
| if (endWithSlashBracket) { |
| proposal = new CustomCompletionProposal("/>", //$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), |
| // this is one of the few times to ignore |
| // the length -- always insert |
| // contentAssistRequest.getReplacementLength(), |
| 0, 2, image, NLS.bind(XMLUIMessages.Close_with__, (new Object[]{" \"/>\""})), //$NON-NLS-1$ |
| null, proposedInfo, XMLRelevanceConstants.R_CLOSE_TAG + 1); // +1 |
| // to |
| // bring |
| // to |
| // top |
| // of |
| // list |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| else if (contentAssistRequest.getDocumentRegion() == node.getLastStructuredDocumentRegion() && !node.getLastStructuredDocumentRegion().isEnded()) { |
| setErrorMessage(null); |
| // prompt with a closing end character for the end tag |
| CustomCompletionProposal proposal = new CustomCompletionProposal(">", //$NON-NLS-1$ |
| contentAssistRequest.getReplacementBeginPosition(), |
| // this is one of the few times to ignore the |
| // length -- always insert |
| // contentAssistRequest.getReplacementLength(), |
| 0, 1, image, NLS.bind(XMLUIMessages.Close_with__, (new Object[]{" '>'"})), //$NON-NLS-1$ |
| null, proposedInfo, XMLRelevanceConstants.R_CLOSE_TAG); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| else if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| setErrorMessage(UNKNOWN_CONTEXT); |
| } |
| } |
| |
| protected void addTagInsertionProposals(ContentAssistRequest contentAssistRequest, int childPosition) { |
| List cmnodes = null; |
| Node parent = contentAssistRequest.getParent(); |
| String error = null; |
| |
| // (nsd) This is only valid at the document element level |
| // only valid if it's XML (check added 2/17/2004) |
| if (parent != null && parent.getNodeType() == Node.DOCUMENT_NODE && ((IDOMDocument) parent).isXMLType() && !isCursorAfterXMLPI(contentAssistRequest)) { |
| return; |
| } |
| // only want proposals if cursor is after doctype... |
| if (!isCursorAfterDoctype(contentAssistRequest)) |
| return; |
| |
| // fix for meta-info comment nodes.. they currently "hide" other |
| // proposals because the don't |
| // have a content model (so can't propose any children..) |
| if (parent != null && parent instanceof IDOMNode && isCommentNode((IDOMNode) parent)) { |
| // loop and find non comment node? |
| while (parent != null && isCommentNode((IDOMNode) parent)) { |
| parent = parent.getParentNode(); |
| } |
| } |
| |
| if (parent.getNodeType() == Node.ELEMENT_NODE) { |
| CMElementDeclaration parentDecl = getCMElementDeclaration(parent); |
| if (parentDecl != null) { |
| // XSD-specific ability - no filtering |
| CMDataType childType = parentDecl.getDataType(); |
| if (childType != null) { |
| String[] childStrings = childType.getEnumeratedValues(); |
| if (childStrings != null) { |
| // the content string is the sole valid child...so |
| // replace the rest |
| int begin = contentAssistRequest.getReplacementBeginPosition(); |
| int length = contentAssistRequest.getReplacementLength(); |
| if (parent instanceof IDOMNode) { |
| if (((IDOMNode) parent).getLastStructuredDocumentRegion() != ((IDOMNode) parent).getFirstStructuredDocumentRegion()) { |
| begin = ((IDOMNode) parent).getFirstStructuredDocumentRegion().getEndOffset(); |
| length = ((IDOMNode) parent).getLastStructuredDocumentRegion().getStartOffset() - begin; |
| } |
| } |
| String proposedInfo = getAdditionalInfo(parentDecl, childType); |
| for (int i = 0; i < childStrings.length; i++) { |
| CustomCompletionProposal textProposal = new CustomCompletionProposal(childStrings[i], begin, length, childStrings[i].length(), XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_ENUM), childStrings[i], null, proposedInfo, XMLRelevanceConstants.R_TAG_INSERTION); |
| contentAssistRequest.addProposal(textProposal); |
| } |
| } |
| } |
| } |
| if (parentDecl != null && parentDecl.getContentType() == CMElementDeclaration.PCDATA) { |
| addPCDATAProposal(parentDecl.getNodeName(), contentAssistRequest); |
| } |
| else { |
| // retrieve the list of all possible children within this parent context |
| cmnodes = getAvailableChildElementDeclarations((Element)parent, childPosition, ModelQueryAction.INSERT); |
| |
| // retrieve the list of the possible children within this parent context and at this index |
| List strictCMNodeSuggestions = null; |
| if (XMLUIPreferenceNames.SUGGESTION_STRATEGY_VALUE_STRICT.equals(XMLUIPlugin.getInstance().getPreferenceStore().getString(XMLUIPreferenceNames.SUGGESTION_STRATEGY))) |
| { |
| strictCMNodeSuggestions = getValidChildElementDeclarations((Element)parent, childPosition, ModelQueryAction.INSERT); |
| } |
| Iterator nodeIterator = cmnodes.iterator(); |
| if (!nodeIterator.hasNext()) { |
| if (getCMElementDeclaration(parent) != null) |
| error = NLS.bind(XMLUIMessages._Has_no_available_child, (new Object[]{parent.getNodeName()})); |
| else |
| error = NLS.bind(XMLUIMessages.Element__is_unknown, (new Object[]{parent.getNodeName()})); |
| } |
| String matchString = contentAssistRequest.getMatchString(); |
| // chop off any leading <'s and whitespace from the |
| // matchstring |
| while ((matchString.length() > 0) && (Character.isWhitespace(matchString.charAt(0)) || beginsWith(matchString, "<"))) //$NON-NLS-1$ |
| //$NON-NLS-1$ |
| matchString = matchString.substring(1); |
| while (nodeIterator.hasNext()) { |
| Object o = nodeIterator.next(); |
| if (o instanceof CMElementDeclaration) { |
| CMElementDeclaration elementDecl = (CMElementDeclaration) o; |
| // only add proposals for the child element's that |
| // begin with the matchstring |
| String tagname = getRequiredName(parent, elementDecl); |
| boolean isStrictCMNodeSuggestion = strictCMNodeSuggestions != null ? strictCMNodeSuggestions.contains(elementDecl) : false; |
| |
| Image image = CMImageUtil.getImage(elementDecl); |
| |
| if (image == null) { |
| if (strictCMNodeSuggestions != null) { |
| image = isStrictCMNodeSuggestion ? |
| XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC_EMPHASIZED) : |
| XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC_DEEMPHASIZED); |
| } |
| else { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| } |
| |
| } |
| |
| //int markupAdjustment = getContentGenerator().getMinimalStartTagLength(parent, elementDecl); |
| if (beginsWith(tagname, matchString)) { |
| String proposedText = getRequiredText(parent, elementDecl); |
| |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=89811 |
| // place cursor in first empty quotes |
| int markupAdjustment = getCursorPositionForProposedText(proposedText); |
| |
| String proposedInfo = getAdditionalInfo(parentDecl, elementDecl); |
| int relevance = isStrictCMNodeSuggestion ? XMLRelevanceConstants.R_STRICTLY_VALID_TAG_INSERTION : XMLRelevanceConstants.R_TAG_INSERTION; |
| CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), markupAdjustment, image, tagname, null, proposedInfo, relevance); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| if (contentAssistRequest.getProposals().size() == 0) { |
| if (error != null) |
| setErrorMessage(error); |
| else if (contentAssistRequest.getMatchString() != null && contentAssistRequest.getMatchString().length() > 0) |
| setErrorMessage(NLS.bind(XMLUIMessages.No_known_child_tag, (new Object[]{parent.getNodeName(), contentAssistRequest.getMatchString()}))); |
| //$NON-NLS-1$ = "No known child tag names of <{0}> begin with \"{1}\"." |
| else |
| setErrorMessage(NLS.bind(XMLUIMessages.__Has_no_known_child, (new Object[]{parent.getNodeName()}))); |
| } |
| } |
| } |
| else if (parent.getNodeType() == Node.DOCUMENT_NODE) { |
| // Can only prompt with elements if the cursor position is past |
| // the XML processing |
| // instruction and DOCTYPE declaration |
| boolean xmlpiFound = false; |
| boolean doctypeFound = false; |
| int minimumOffset = -1; |
| |
| for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
| |
| boolean xmlpi = (child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE && child.getNodeName().equals("xml")); //$NON-NLS-1$ |
| boolean doctype = child.getNodeType() == Node.DOCUMENT_TYPE_NODE; |
| if (xmlpi || doctype && minimumOffset < 0) |
| minimumOffset = ((IDOMNode) child).getFirstStructuredDocumentRegion().getStartOffset() + ((IDOMNode) child).getFirstStructuredDocumentRegion().getTextLength(); |
| xmlpiFound = xmlpiFound || xmlpi; |
| doctypeFound = doctypeFound || doctype; |
| } |
| |
| if (contentAssistRequest.getReplacementBeginPosition() >= minimumOffset) { |
| List childDecls = getAvailableRootChildren((Document) parent, childPosition); |
| for (int i = 0; i < childDecls.size(); i++) { |
| CMElementDeclaration ed = (CMElementDeclaration) childDecls.get(i); |
| if (ed != null) { |
| Image image = CMImageUtil.getImage(ed); |
| if (image == null) { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| } |
| String proposedText = getRequiredText(parent, ed); |
| String tagname = getRequiredName(parent, ed); |
| // account for the < and > |
| int markupAdjustment = getContentGenerator().getMinimalStartTagLength(parent, ed); |
| String proposedInfo = getAdditionalInfo(null, ed); |
| CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), markupAdjustment, image, tagname, null, proposedInfo, XMLRelevanceConstants.R_TAG_INSERTION); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| } |
| } |
| |
| protected void addTagNameProposals(ContentAssistRequest contentAssistRequest, int childPosition) { |
| List cmnodes = null; |
| Node parent = contentAssistRequest.getParent(); |
| IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); |
| String error = null; |
| String matchString = contentAssistRequest.getMatchString(); |
| if (parent.getNodeType() == Node.ELEMENT_NODE) { |
| // retrieve the list of children |
| //validActions = getAvailableChildrenAtIndex((Element) parent, childPosition); |
| cmnodes = getAvailableChildElementDeclarations((Element)parent, childPosition, ModelQueryAction.INSERT); |
| Iterator nodeIterator = cmnodes.iterator(); |
| // chop off any leading <'s and whitespace from the matchstring |
| while ((matchString.length() > 0) && (Character.isWhitespace(matchString.charAt(0)) || beginsWith(matchString, "<"))) //$NON-NLS-1$ |
| //$NON-NLS-1$ |
| matchString = matchString.substring(1); |
| if (!nodeIterator.hasNext()) |
| error = NLS.bind(XMLUIMessages.__Has_no_known_child, (new Object[]{parent.getNodeName()})); |
| while (nodeIterator.hasNext()) { |
| CMNode elementDecl = (CMNode) nodeIterator.next(); |
| if (elementDecl != null) { |
| // only add proposals for the child element's that begin |
| // with the matchstring |
| String proposedText = null; |
| int cursorAdjustment = 0; |
| |
| // do a check to see if partial attributes of partial tag |
| // names are in list |
| if ((node != null && node.getAttributes() != null && node.getAttributes().getLength() > 0 && attributeInList(node, parent, elementDecl)) || ((node.getNodeType() != Node.TEXT_NODE) && node.getFirstStructuredDocumentRegion().isEnded())) { |
| |
| proposedText = getRequiredName(parent, elementDecl); |
| cursorAdjustment = proposedText.length(); |
| } |
| else { |
| proposedText = getRequiredName(parent, elementDecl); |
| cursorAdjustment = proposedText.length(); |
| if (elementDecl instanceof CMElementDeclaration) { |
| CMElementDeclaration ed = (CMElementDeclaration) elementDecl; |
| if (ed.getContentType() == CMElementDeclaration.EMPTY) { |
| proposedText += getContentGenerator().getStartTagClose(parent, ed); |
| cursorAdjustment = proposedText.length(); |
| } |
| else { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=89811 |
| StringBuffer sb = new StringBuffer(); |
| getContentGenerator().generateTag(parent, ed, sb); |
| // since it's a name proposal, assume '<' is already there |
| // only return the rest of the tag |
| proposedText = sb.toString().substring(1); |
| cursorAdjustment = getCursorPositionForProposedText(proposedText); |
| |
| //cursorAdjustment = proposedText.length() + 1; |
| //proposedText += "></" + getRequiredName(parent, elementDecl) + ">"; //$NON-NLS-2$//$NON-NLS-1$ |
| } |
| } |
| } |
| if (beginsWith(proposedText, matchString)) { |
| Image image = CMImageUtil.getImage(elementDecl); |
| if (image == null) { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| } |
| String proposedInfo = getAdditionalInfo(getCMElementDeclaration(parent), elementDecl); |
| CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), cursorAdjustment, image, getRequiredName(parent, elementDecl), null, proposedInfo, XMLRelevanceConstants.R_TAG_NAME); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| if (contentAssistRequest.getProposals().size() == 0) { |
| if (error != null) |
| setErrorMessage(error); |
| else if (contentAssistRequest.getMatchString() != null && contentAssistRequest.getMatchString().length() > 0) |
| setErrorMessage(NLS.bind(XMLUIMessages.No_known_child_tag_names, (new Object[]{parent.getNodeName(), contentAssistRequest.getMatchString()}))); |
| //$NON-NLS-1$ = "No known child tag names of <{0}> begin with \"{1}\"" |
| else |
| setErrorMessage(NLS.bind(XMLUIMessages.__Has_no_known_child, (new Object[]{parent.getNodeName()}))); |
| } |
| } |
| else if (parent.getNodeType() == Node.DOCUMENT_NODE) { |
| List childElements = getAvailableRootChildren((Document) parent, childPosition); |
| for (int i = 0; i < childElements.size(); i++) { |
| CMNode ed = (CMNode) childElements.get(i); |
| if (ed == null) |
| continue; |
| String proposedText = null; |
| int cursorAdjustment = 0; |
| if(ed instanceof CMElementDeclaration) { |
| // proposedText = getRequiredName(parent, ed); |
| StringBuffer sb = new StringBuffer(); |
| getContentGenerator().generateTag(parent, (CMElementDeclaration)ed, sb); |
| // tag starts w/ '<', but we want to compare to name |
| proposedText = sb.toString().substring(1); |
| |
| if (!beginsWith(proposedText, matchString)) |
| continue; |
| |
| cursorAdjustment = getCursorPositionForProposedText(proposedText); |
| |
| if (ed instanceof CMElementDeclaration) { |
| CMElementDeclaration elementDecl = (CMElementDeclaration) ed; |
| if (elementDecl.getContentType() == CMElementDeclaration.EMPTY) { |
| proposedText += getContentGenerator().getStartTagClose(parent, elementDecl); |
| cursorAdjustment = proposedText.length(); |
| } |
| } |
| |
| String proposedInfo = getAdditionalInfo(null, ed); |
| Image image = CMImageUtil.getImage(ed); |
| if (image == null) { |
| image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC); |
| } |
| CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), cursorAdjustment, image, getRequiredName(parent, ed), null, proposedInfo, XMLRelevanceConstants.R_TAG_NAME); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| } |
| /** |
| * this is the position the cursor should be in after the proposal is applied |
| * @param proposedText |
| * @return the position the cursor should be in after the proposal is applied |
| */ |
| private int getCursorPositionForProposedText(String proposedText) { |
| int cursorAdjustment; |
| cursorAdjustment = proposedText.indexOf("\"\"") + 1; //$NON-NLS-1$ |
| // otherwise, after the first tag |
| if(cursorAdjustment==0) |
| cursorAdjustment = proposedText.indexOf('>') + 1; |
| if(cursorAdjustment==0) |
| cursorAdjustment = proposedText.length() + 1; |
| |
| return cursorAdjustment; |
| } |
| |
| /** |
| * @deprecated XML proposal is added via xml declaration template instead |
| */ |
| protected void addXMLProposal(ContentAssistRequest contentAssistRequest) { |
| String proposedText = "<?xml version=\"1.0\" encoding=\"" + ContentTypeEncodingPreferences.getUserPreferredCharsetName(ContentTypeIdForXML.ContentTypeID_XML) + "\"?>"; //$NON-NLS-2$//$NON-NLS-1$ |
| ICompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), proposedText.length(), XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_PROCESSINGINSTRUCTION), proposedText, null, null, XMLRelevanceConstants.R_XML_DECLARATION); |
| // TODO add special XML proposal info |
| contentAssistRequest.addProposal(proposal); |
| } |
| |
| /** |
| * This method determines if any of the attributes in the proposed XMLNode |
| * node, are possible values of attributes from possible Elements at this |
| * point in the document according to the Content Model. |
| * |
| * @param node |
| * the element with attributes that you would like to test if |
| * are possible for possible Elements at this point |
| * @param cmnode |
| * possible element at this point in the document (depending on |
| * what 'node' is) true if any attributes of 'node' match any |
| * possible attributes from 'cmnodes' list. |
| */ |
| protected boolean attributeInList(IDOMNode node, Node parent, CMNode cmnode) { |
| if (node == null || parent == null || cmnode == null) |
| return false; |
| String elementMatchString = node.getNodeName(); |
| String cmnodeName = getRequiredName(parent, cmnode);// cmnode.getNodeName(); |
| if (node instanceof Element) { |
| NamedNodeMap map = ((Element) node).getAttributes(); |
| String attrMatchString = ""; //$NON-NLS-1$ |
| CMNamedNodeMap cmattrMap = null; |
| // iterate attribute possibilities for partially started node |
| for (int i = 0; map != null && i < map.getLength(); i++) { |
| attrMatchString = map.item(i).getNodeName(); |
| // filter on whatever user typed for element name already |
| if (beginsWith(cmnodeName, elementMatchString)) { |
| if (cmnode.getNodeType() == CMNode.ELEMENT_DECLARATION) { |
| cmattrMap = ((CMElementDeclaration) cmnode).getAttributes(); |
| // iterate possible attributes from a cmnode in |
| // proposal list |
| for (int k = 0; cmattrMap != null && k < cmattrMap.getLength(); k++) { |
| // check if name matches |
| if (cmattrMap.item(k).getNodeName().equals(attrMatchString)) { |
| return true; |
| } |
| } |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| protected boolean beginsWith(String aString, String prefix) { |
| if (aString == null || prefix == null) |
| return true; |
| // (pa) matching independent of case to be consistant with Java |
| // editor CA |
| return aString.toLowerCase().startsWith(prefix.toLowerCase()); |
| } |
| |
| protected ContentAssistRequest computeAttributeProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| if (documentPosition < sdRegion.getStartOffset(completionRegion)) { |
| // setup to insert new attributes |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| // Setup to replace an existing attribute name |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| } |
| addAttributeNameProposals(contentAssistRequest); |
| contentAssistRequest.setReplacementBeginPosition(documentPosition); |
| contentAssistRequest.setReplacementLength(0); |
| if (node.getFirstStructuredDocumentRegion() != null && (!node.getFirstStructuredDocumentRegion().isEnded())) { |
| addTagCloseProposals(contentAssistRequest); |
| } |
| return contentAssistRequest; |
| } |
| |
| protected ContentAssistRequest computeAttributeValueProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| if (documentPosition > sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength() && sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength() != sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()) { |
| // setup to add a new attribute at the documentPosition |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addAttributeNameProposals(contentAssistRequest); |
| if (actualNode.getFirstStructuredDocumentRegion() != null && !actualNode.getFirstStructuredDocumentRegion().isEnded()) { |
| addTagCloseProposals(contentAssistRequest); |
| } |
| } |
| else { |
| // setup to replace the existing value |
| if (!nodeAtOffset.getFirstStructuredDocumentRegion().isEnded() && documentPosition < sdRegion.getStartOffset(completionRegion)) { |
| // if the IStructuredDocumentRegion isn't closed and the |
| // cursor is in front of the value, add |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addAttributeNameProposals(contentAssistRequest); |
| } |
| else { |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| addAttributeValueProposals(contentAssistRequest); |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| protected ContentAssistRequest computeCompletionProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode treeNode, IDOMNode xmlnode) { |
| ContentAssistRequest contentAssistRequest = null; |
| String regionType = completionRegion.getType(); |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| |
| // Handle the most common and best supported cases |
| if (xmlnode.getNodeType() == Node.ELEMENT_NODE || xmlnode.getNodeType() == Node.DOCUMENT_NODE) { |
| if (regionType == DOMRegionContext.XML_TAG_OPEN) { |
| contentAssistRequest = computeTagOpenProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_NAME) { |
| contentAssistRequest = computeTagNameProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) { |
| contentAssistRequest = computeAttributeProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { |
| contentAssistRequest = computeEqualsProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE && documentPosition == sdRegion.getTextEndOffset() && (sdRegion.getText(completionRegion).endsWith("\"") || sdRegion.getText(completionRegion).endsWith("\'"))) //$NON-NLS-1$ //$NON-NLS-2$ |
| { |
| // this is for when the cursor is at the end of the closing |
| // quote for an attribute.. |
| IDOMNode actualNode = (IDOMNode) xmlnode.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagCloseProposals(contentAssistRequest); |
| } |
| else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) { |
| contentAssistRequest = computeAttributeValueProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if ((regionType == DOMRegionContext.XML_TAG_CLOSE) || (regionType == DOMRegionContext.XML_EMPTY_TAG_CLOSE) || (regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_CLOSE))) { |
| contentAssistRequest = computeTagCloseProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) { |
| contentAssistRequest = computeEndTagOpenProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| else if (regionType == DOMRegionContext.XML_CONTENT || regionType == DOMRegionContext.XML_CHAR_REFERENCE || regionType == DOMRegionContext.XML_ENTITY_REFERENCE || regionType == DOMRegionContext.XML_PE_REFERENCE) { |
| contentAssistRequest = computeContentProposals(documentPosition, matchString, completionRegion, treeNode, xmlnode); |
| } |
| |
| // These ITextRegion types begin DOM Elements as well and although |
| // internally harder to assist, |
| // text insertions directly before them can be made |
| else if (documentPosition == sdRegion.getStartOffset(completionRegion) && (regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_COMMENT_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DECLARATION_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_EXPRESSION_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_SCRIPTLET_OPEN) || regionType == DOMRegionContext.XML_DECLARATION_OPEN || regionType == DOMRegionContext.XML_PI_OPEN || regionType == DOMRegionContext.XML_COMMENT_OPEN || regionType == DOMRegionContext.XML_CDATA_OPEN)) { |
| contentAssistRequest = newContentAssistRequest(treeNode, xmlnode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPositionForModelQuery(treeNode)); |
| addStartDocumentProposals(contentAssistRequest); |
| } |
| } |
| // Not a Document or Element? (odd cases go here for now) |
| else if (isCloseRegion(completionRegion)) { |
| contentAssistRequest = newContentAssistRequest(treeNode, xmlnode.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion) + completionRegion.getLength(), 0, matchString); |
| addStartDocumentProposals(contentAssistRequest); |
| if (documentPosition >= sdRegion.getTextEndOffset(completionRegion)) |
| addTagInsertionProposals(contentAssistRequest, getElementPositionForModelQuery(treeNode) + 1); |
| } |
| else if (documentPosition == sdRegion.getStartOffset(completionRegion) && (regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_COMMENT_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DECLARATION_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_EXPRESSION_OPEN) || regionType.equals(DOMJSPRegionContextsPrivateCopy.JSP_SCRIPTLET_OPEN) || regionType == DOMRegionContext.XML_DECLARATION_OPEN || regionType == DOMRegionContext.XML_PI_OPEN || regionType == DOMRegionContext.XML_COMMENT_OPEN || regionType == DOMRegionContext.XML_CDATA_OPEN)) { |
| contentAssistRequest = newContentAssistRequest(treeNode, xmlnode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPositionForModelQuery(treeNode)); |
| addStartDocumentProposals(contentAssistRequest); |
| } |
| return contentAssistRequest; |
| } |
| |
| /** |
| * CONTENT ASSIST STARTS HERE |
| * |
| * Return a list of proposed code completions based on the specified |
| * location within the document that corresponds to the current cursor |
| * position within the text-editor control. |
| * |
| * @param textViewer |
| * @param documentPosition - |
| * the cursor location within the document |
| * |
| * an array of ICompletionProposals |
| */ |
| public ICompletionProposal[] computeCompletionProposals(ITextViewer textViewer, int documentPosition) { |
| |
| setErrorMessage(null); |
| |
| fTextViewer = textViewer; |
| |
| IndexedRegion treeNode = ContentAssistUtils.getNodeAt(textViewer, documentPosition); |
| |
| Node node = (Node) treeNode; |
| while (node != null && node.getNodeType() == Node.TEXT_NODE && node.getParentNode() != null) |
| node = node.getParentNode(); |
| IDOMNode xmlnode = (IDOMNode) node; |
| |
| ContentAssistRequest contentAssistRequest = null; |
| |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| ITextRegion completionRegion = getCompletionRegion(documentPosition, node); |
| |
| String matchString = getMatchString(sdRegion, completionRegion, documentPosition); |
| |
| // Handle empty Documents |
| if (completionRegion == null) { |
| if (((treeNode == null) || ((Node) treeNode).getNodeType() == Node.DOCUMENT_NODE) && completionRegion == null && (xmlnode == null || xmlnode.getChildNodes() == null || xmlnode.getChildNodes().getLength() == 0)) { |
| IStructuredModel sModel = StructuredModelManager.getModelManager().getExistingModelForRead(textViewer.getDocument()); |
| try { |
| if (sModel != null) { |
| IDOMDocument docNode = ((IDOMModel) sModel).getDocument(); |
| contentAssistRequest = newContentAssistRequest(docNode, docNode, sdRegion, completionRegion, documentPosition, 0, null); |
| addEmptyDocumentProposals(contentAssistRequest); |
| } |
| } |
| finally { |
| if (sModel != null) |
| sModel.releaseFromRead(); |
| } |
| if (contentAssistRequest == null) { |
| Logger.logException(new IllegalStateException("problem getting model")); //$NON-NLS-1$ |
| return new ICompletionProposal[0]; |
| } |
| return contentAssistRequest.getCompletionProposals(); |
| } |
| // MASSIVE ERROR CONDITION |
| Logger.logException(new IllegalStateException("completion region was null")); //$NON-NLS-1$ |
| setErrorMessage(INTERNALERROR); |
| contentAssistRequest = newContentAssistRequest((Node) treeNode, node.getParentNode(), sdRegion, completionRegion, documentPosition, 0, ""); //$NON-NLS-1$ |
| return contentAssistRequest.getCompletionProposals(); |
| } |
| |
| |
| // catch documents where no region can be determined |
| if (xmlnode.getNodeType() == Node.DOCUMENT_NODE && (completionRegion == null || xmlnode.getChildNodes() == null || xmlnode.getChildNodes().getLength() == 0)) { |
| contentAssistRequest = computeStartDocumentProposals(documentPosition, matchString, completionRegion, (IDOMNode) treeNode, xmlnode); |
| return contentAssistRequest.getCompletionProposals(); |
| } |
| |
| // compute normal proposals |
| contentAssistRequest = computeCompletionProposals(documentPosition, matchString, completionRegion, (IDOMNode) treeNode, xmlnode); |
| if (contentAssistRequest == null) { |
| contentAssistRequest = newContentAssistRequest((Node) treeNode, node.getParentNode(), sdRegion, completionRegion, documentPosition, 0, ""); //$NON-NLS-1$ |
| if (Debug.displayWarnings) |
| System.out.println(UNKNOWN_CONTEXT + " " + completionRegion.getType() + "@" + documentPosition); //$NON-NLS-2$//$NON-NLS-1$ |
| setErrorMessage(UNKNOWN_CONTEXT); |
| } |
| |
| if (contentAssistRequest.getProposals().size() == 0) |
| setErrorMessage(UNKNOWN_CONTEXT); |
| |
| return contentAssistRequest.getCompletionProposals(); |
| } |
| |
| protected ContentAssistRequest computeContentProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| |
| // setup to add children at the content node's position |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, getStructuredDocumentRegion(documentPosition), completionRegion, documentPosition, 0, matchString); |
| if (node != null && node.getNodeType() == Node.DOCUMENT_NODE && ((Document) node).getDoctype() == null) |
| addStartDocumentProposals(contentAssistRequest); |
| addTagInsertionProposals(contentAssistRequest, getElementPositionForModelQuery(nodeAtOffset)); |
| if (node.getNodeType() != Node.DOCUMENT_NODE) { |
| addEndTagProposals(contentAssistRequest); |
| } |
| // entities? |
| addEntityProposals(contentAssistRequest, documentPosition, completionRegion, node); |
| // addEntityProposals(contentAssistRequest); |
| return contentAssistRequest; |
| } |
| |
| /** |
| * Returns information about possible contexts based on the specified |
| * location within the document that corresponds to the current cursor |
| * position within the text viewer. |
| * |
| * @param viewer |
| * the viewer whose document is used to compute the possible |
| * contexts an array of context information objects or |
| * <code>null</code> if no context could be found |
| */ |
| public IContextInformation[] computeContextInformation(ITextViewer viewer, int documentOffset) { |
| if (fAttributeInfoProvider == null) |
| fAttributeInfoProvider = new AttributeContextInformationProvider((IStructuredDocument) viewer.getDocument(), (AttributeContextInformationPresenter) getContextInformationValidator()); |
| return fAttributeInfoProvider.getAttributeInformation(documentOffset); |
| } |
| |
| protected ContentAssistRequest computeEndTagOpenProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| int completionRegionStart = sdRegion.getStartOffset(completionRegion); |
| int completionRegionLength = completionRegion.getLength(); |
| IStructuredDocumentRegion sdRegionAtCompletionOffset = node.getStructuredDocument().getRegionAtCharacterOffset(completionRegionStart + completionRegionLength); |
| ITextRegion regionAtEndOfCompletion = sdRegionAtCompletionOffset.getRegionAtCharacterOffset(completionRegionStart + completionRegionLength); |
| |
| if (documentPosition != completionRegionStart && regionAtEndOfCompletion != null && regionAtEndOfCompletion.getType() == DOMRegionContext.XML_TAG_NAME) { |
| ITextRegion nameRegion = regionAtEndOfCompletion; |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, nodeAtOffset.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(nameRegion), nameRegion.getTextLength(), matchString); |
| } |
| else { |
| if (nodeAtOffset.getFirstStructuredDocumentRegion() == sdRegion) { |
| // abnormal case, this unmatched end tag will be a sibling |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, nodeAtOffset.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| // normal case, this end tag is the parent |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, nodeAtOffset, sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| } |
| // if (documentPosition >= sdRegion.getStartOffset(completionRegion) + |
| // completionRegion.getTextLength()) |
| addEndTagProposals(contentAssistRequest); |
| // else |
| if (completionRegionStart == documentPosition) { |
| // positioned at start of end tag |
| addTagInsertionProposals(contentAssistRequest, node.getChildNodes().getLength()); |
| } |
| return contentAssistRequest; |
| } |
| |
| /** |
| * return all possible EntityReferenceProposals (according to current |
| * position in doc) |
| */ |
| protected ICompletionProposal[] computeEntityReferenceProposals(int documentPosition, ITextRegion completionRegion, IDOMNode treeNode) { |
| // only handle XML content for now |
| Vector proposals = new Vector(); // ICompletionProposals |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| if (completionRegion != null && completionRegion.getType() == DOMRegionContext.XML_CONTENT) { |
| int nodeOffset = documentPosition - sdRegion.getStartOffset(completionRegion); |
| String regionText = sdRegion.getFullText(completionRegion); |
| |
| // if directly to the right of a &, region will be null, need to |
| // move to |
| // the previous region...there might be a better way to do this |
| if (regionText != null && regionText.trim().equals("") && documentPosition > 0) { //$NON-NLS-1$ |
| IStructuredDocumentRegion prev = treeNode.getStructuredDocument().getRegionAtCharacterOffset(documentPosition - 1); |
| if (prev != null && prev.getFullText().trim().equals("&")) { //$NON-NLS-1$ |
| completionRegion = prev; |
| regionText = prev.getFullText(); |
| nodeOffset = 1; |
| } |
| } |
| |
| // string must start w/ & |
| if (regionText != null && regionText.startsWith("&")) { //$NON-NLS-1$ |
| String key = (nodeOffset > 0) ? regionText.substring(1, nodeOffset) : ""; //$NON-NLS-1$ |
| |
| // get entity proposals, passing in the appropriate start |
| // string |
| ModelQuery mq = ModelQueryUtil.getModelQuery(((Node) treeNode).getOwnerDocument()); |
| if (mq != null) { |
| CMDocument xmlDoc = mq.getCorrespondingCMDocument(treeNode); |
| CMNamedNodeMap cmmap = null; |
| Properties entities = null; |
| if (xmlDoc != null) { |
| cmmap = xmlDoc.getEntities(); |
| } |
| if (cmmap != null) { |
| entities = mapToProperties(cmmap); |
| } |
| else // 224787 in absence of content model, just use |
| // minimal 5 entities |
| { |
| entities = new Properties(); |
| entities.put("quot", "\""); //$NON-NLS-1$ //$NON-NLS-2$ |
| entities.put("apos", "'"); //$NON-NLS-1$ //$NON-NLS-2$ |
| entities.put("amp", "&"); //$NON-NLS-1$ //$NON-NLS-2$ |
| entities.put("lt", "<"); //$NON-NLS-1$ //$NON-NLS-2$ |
| entities.put("gt", ">"); //$NON-NLS-1$ //$NON-NLS-2$ |
| entities.put("nbsp", " "); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| addEntityProposals(proposals, entities, key, nodeOffset, sdRegion, completionRegion); //$NON-NLS-1$ |
| } |
| } |
| } |
| return (ICompletionProposal[]) ((proposals.size() > 0) ? proposals.toArray(new ICompletionProposal[proposals.size()]) : null); |
| } |
| |
| protected ContentAssistRequest computeEqualsProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| ITextRegion valueRegion = node.getStartStructuredDocumentRegion().getRegionAtCharacterOffset(sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()); |
| if (valueRegion != null && valueRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE && sdRegion.getStartOffset(valueRegion) <= documentPosition) { |
| // replace the adjacent attribute value |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, valueRegion, sdRegion.getStartOffset(valueRegion), valueRegion.getTextLength(), matchString); |
| } |
| else { |
| // append an attribute value after the '=' |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| addAttributeValueProposals(contentAssistRequest); |
| return contentAssistRequest; |
| } |
| |
| protected ContentAssistRequest computeStartDocumentProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| // setup for a non-empty document, but one that hasn't been formally |
| // started |
| ContentAssistRequest contentAssistRequest = null; |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, getStructuredDocumentRegion(documentPosition), completionRegion, documentPosition, 0, matchString); |
| addStartDocumentProposals(contentAssistRequest); |
| return contentAssistRequest; |
| } |
| |
| protected ContentAssistRequest computeTagCloseProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| |
| if ((node.getNodeType() == Node.DOCUMENT_NODE) || (documentPosition >= sdRegion.getEndOffset())) { |
| // this is a content request as the documentPosition is AFTER the |
| // end of the closing region |
| if (node == nodeAtOffset && node.getParentNode() != null) |
| node = (IDOMNode) node.getParentNode(); |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPositionForModelQuery(nodeAtOffset)); |
| if (node.getNodeType() != Node.DOCUMENT_NODE && node.getEndStructuredDocumentRegion() == null) { |
| addEndTagProposals(contentAssistRequest); |
| } |
| } |
| else { |
| // at the start of the tag's close or within it |
| ITextRegion closeRegion = sdRegion.getLastRegion(); |
| boolean insideTag = !sdRegion.isEnded() || documentPosition <= sdRegion.getStartOffset(closeRegion); |
| if (insideTag) { |
| // this is a request for completions within a tag |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition, 0, matchString); |
| if (node.getNodeType() != Node.DOCUMENT_NODE && node.getEndStructuredDocumentRegion() != null) { |
| addTagCloseProposals(contentAssistRequest); |
| } |
| if (sdRegion == nodeAtOffset.getFirstStructuredDocumentRegion()) { |
| contentAssistRequest.setReplacementBeginPosition(documentPosition); |
| contentAssistRequest.setReplacementLength(0); |
| addAttributeNameProposals(contentAssistRequest); |
| } |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| protected ContentAssistRequest computeTagNameProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| |
| if (sdRegion != nodeAtOffset.getFirstStructuredDocumentRegion()) { |
| // completing the *first* tag in "<tagname1 |<tagname2" |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| if (actualNode != null) { |
| if (actualNode.getFirstStructuredDocumentRegion() == sdRegion) { |
| // start tag |
| if (documentPosition > sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()) { |
| // it's attributes |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode, sdRegion, completionRegion, documentPosition - matchString.length(), matchString.length(), matchString); |
| if (node.getStructuredDocument().getRegionAtCharacterOffset(sdRegion.getStartOffset(completionRegion) - 1).getRegionAtCharacterOffset(sdRegion.getStartOffset(completionRegion) - 1).getType() == DOMRegionContext.XML_TAG_OPEN) |
| addAttributeNameProposals(contentAssistRequest); |
| addTagCloseProposals(contentAssistRequest); |
| } |
| else { |
| // it's name |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, documentPosition - matchString.length(), matchString.length(), matchString); |
| addTagNameProposals(contentAssistRequest, getElementPositionForModelQuery(actualNode)); |
| } |
| } |
| else { |
| if (documentPosition >= sdRegion.getStartOffset(completionRegion) + completionRegion.getLength()) { |
| // insert name |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| // replace name |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| } |
| addEndTagNameProposals(contentAssistRequest); |
| } |
| } |
| } |
| else { |
| if (documentPosition > sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength()) { |
| // unclosed tag with only a name; should prompt for attributes |
| // and a close instead |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node, sdRegion, completionRegion, documentPosition - matchString.length(), matchString.length(), matchString); |
| addAttributeNameProposals(contentAssistRequest); |
| addTagCloseProposals(contentAssistRequest); |
| } |
| else { |
| if (sdRegion.getRegions().get(0).getType() != DOMRegionContext.XML_END_TAG_OPEN) { |
| int replaceLength = documentPosition - sdRegion.getStartOffset(completionRegion); |
| contentAssistRequest = newContentAssistRequest(node, node.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), replaceLength, matchString); |
| addTagNameProposals(contentAssistRequest, getElementPositionForModelQuery(nodeAtOffset)); |
| } |
| else { |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(documentPosition); |
| if (actualNode != null) { |
| if (documentPosition >= sdRegion.getStartOffset(completionRegion) + completionRegion.getTextLength()) { |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| } |
| else { |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| } |
| addEndTagNameProposals(contentAssistRequest); |
| } |
| } |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| protected ContentAssistRequest computeTagOpenProposals(int documentPosition, String matchString, ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node) { |
| ContentAssistRequest contentAssistRequest = null; |
| IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition); |
| if (sdRegion != nodeAtOffset.getFirstStructuredDocumentRegion()) { |
| // completing the *first* XML_TAG_OPEN in "<<tagname" |
| IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(sdRegion.getStartOffset(completionRegion)); |
| if (actualNode != null) { |
| contentAssistRequest = newContentAssistRequest(actualNode, actualNode.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagNameProposals(contentAssistRequest, getElementPositionForModelQuery(actualNode)); |
| addEndTagProposals(contentAssistRequest); // (pa) 220850 |
| } |
| } |
| else { |
| if (documentPosition == sdRegion.getStartOffset(completionRegion)) { |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| // at the start of an existing tag, right before the '<' |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node.getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addTagInsertionProposals(contentAssistRequest, getElementPositionForModelQuery(nodeAtOffset)); |
| addEndTagProposals(contentAssistRequest); |
| } |
| else if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| // at the opening of the VERY first tag with a '<' |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, node.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), matchString); |
| addStartDocumentProposals(contentAssistRequest); |
| } |
| } |
| else { |
| // within the white space |
| ITextRegion name = getNameRegion(node.getStartStructuredDocumentRegion()); |
| // (pa) ITextRegion refactor |
| // if (name != null && name.containsOffset(documentPosition)) |
| // { |
| if (name != null && (sdRegion.getStartOffset(name) <= documentPosition && sdRegion.getEndOffset(name) >= documentPosition)) { |
| // replace the existing name |
| contentAssistRequest = newContentAssistRequest(node, node.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(name), name.getTextLength(), matchString); |
| } |
| else { |
| // insert a valid new name, or possibly an end tag |
| contentAssistRequest = newContentAssistRequest(nodeAtOffset, ((Node) nodeAtOffset).getParentNode(), sdRegion, completionRegion, documentPosition, 0, matchString); |
| addEndTagProposals(contentAssistRequest); |
| contentAssistRequest.setReplacementBeginPosition(documentPosition); |
| contentAssistRequest.setReplacementLength(0); |
| } |
| addTagNameProposals(contentAssistRequest, getElementPositionForModelQuery(nodeAtOffset)); |
| } |
| } |
| return contentAssistRequest; |
| } |
| |
| /** |
| * Retreives cmnode's documentation to display in the completion |
| * proposal's additional info. If no documentation exists for cmnode, try |
| * displaying parentOrOwner's documentation |
| * |
| * String any documentation information to display for cmnode. |
| * <code>null</code> if there is nothing to display. |
| */ |
| protected String getAdditionalInfo(CMNode parentOrOwner, CMNode cmnode) { |
| String addlInfo = null; |
| |
| if (cmnode == null) { |
| if (Debug.displayWarnings) { |
| new IllegalArgumentException("Null declaration!").printStackTrace(); //$NON-NLS-1$ |
| } |
| return null; |
| } |
| |
| addlInfo = getInfoProvider().getInfo(cmnode); |
| if (addlInfo == null && parentOrOwner != null) |
| addlInfo = getInfoProvider().getInfo(parentOrOwner); |
| return addlInfo; |
| } |
| |
| // returns a list of ModelQueryActions |
| protected List getAvailableChildrenAtIndex(Element parent, int index, int validityChecking) { |
| List list = new ArrayList(); |
| CMElementDeclaration parentDecl = getCMElementDeclaration(parent); |
| if (parentDecl != null) { |
| ModelQuery modelQuery = ModelQueryUtil.getModelQuery(parent.getOwnerDocument()); |
| // taken from ActionManagers |
| // int editMode = modelQuery.getEditMode(); |
| int editMode = ModelQuery.EDIT_MODE_UNCONSTRAINED; |
| int ic = (editMode == ModelQuery.EDIT_MODE_CONSTRAINED_STRICT) ? ModelQuery.INCLUDE_CHILD_NODES | ModelQuery.INCLUDE_SEQUENCE_GROUPS : ModelQuery.INCLUDE_CHILD_NODES; |
| modelQuery.getInsertActions(parent, parentDecl, index, ic, validityChecking, list); |
| } |
| return list; |
| } |
| |
| // returns a list of CMElementDeclarations |
| protected List getAvailableRootChildren(Document document, int childIndex) { |
| List list = null; |
| |
| // extract the valid 'root' node name from the DocumentType Node |
| DocumentType docType = document.getDoctype(); |
| String rootName = null; |
| if (docType != null) { |
| rootName = docType.getNodeName(); |
| } |
| if (rootName == null) |
| return new ArrayList(0); |
| |
| for (Node child = document.getFirstChild(); child != null; child = child.getNextSibling()) { |
| // make sure the "root" Element isn't already present |
| // is it required to be an Element? |
| if (child.getNodeType() == Node.ELEMENT_NODE && stringsEqual(child.getNodeName(), rootName)) { |
| // if the node is missing either the start or end tag, don't |
| // count it as present |
| if (child instanceof IDOMNode && (((IDOMNode) child).getStartStructuredDocumentRegion() == null || ((IDOMNode) child).getEndStructuredDocumentRegion() == null)) |
| continue; |
| if (Debug.displayInfo) |
| System.out.println(rootName + " already present!"); //$NON-NLS-1$ |
| setErrorMessage(NLS.bind(XMLUIMessages.The_document_element__, (new Object[]{rootName}))); |
| return new ArrayList(0); |
| } |
| } |
| |
| list = new ArrayList(1); |
| ModelQuery modelQuery = ModelQueryUtil.getModelQuery(document); |
| if (modelQuery != null) { |
| CMDocument cmdoc = modelQuery.getCorrespondingCMDocument(document); |
| if (cmdoc != null) { |
| if (rootName != null) { |
| CMElementDeclaration rootDecl = (CMElementDeclaration) cmdoc.getElements().getNamedItem(rootName); |
| if (rootDecl != null) { |
| list.add(rootDecl); |
| } |
| else { |
| // supply the given document name anyway, even if it |
| // is an error |
| list.add(new SimpleCMElementDeclaration(rootName)); |
| if (Debug.displayInfo || Debug.displayWarnings) |
| System.out.println("No definition found for " + rootName + " in " + docType.getPublicId() + "/" + docType.getSystemId()); //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ |
| String location = "" + (docType.getPublicId() != null ? docType.getPublicId() + "/" : "") + (docType.getSystemId() != null ? docType.getSystemId() : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ |
| //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ |
| if (location.length() > 0) |
| setErrorMessage(NLS.bind(XMLUIMessages.No_definition_for_in, (new Object[]{rootName, location}))); |
| else |
| setErrorMessage(NLS.bind(XMLUIMessages.No_definition_for, (new Object[]{rootName}))); |
| } |
| } |
| } |
| else { |
| if (Debug.displayInfo || Debug.displayWarnings) |
| System.out.println("No content model found."); //$NON-NLS-1$ |
| //$NON-NLS-1$ |
| //$NON-NLS-1$ |
| String location = "" + (docType.getPublicId() != null ? docType.getPublicId() + "/" : "") + (docType.getSystemId() != null ? docType.getSystemId() : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ |
| //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ |
| if (location.length() > 0) |
| setErrorMessage(NLS.bind(XMLUIMessages.No_content_model_for, (new Object[]{location}))); |
| else |
| setErrorMessage(XMLUIMessages.No_content_model_found_UI_); |
| } |
| } |
| |
| return list; |
| } |
| |
| protected 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; |
| } |
| |
| /** |
| * Returns the characters which when entered by the user should |
| * automatically trigger the presentation of possible completions. |
| * |
| * the auto activation characters for completion proposal or |
| * <code>null</code> if no auto activation is desired |
| */ |
| public char[] getCompletionProposalAutoActivationCharacters() { |
| return completionProposalAutoActivationCharacters; |
| } |
| |
| protected ITextRegion getCompletionRegion(int offset, IStructuredDocumentRegion sdRegion) { |
| ITextRegion region = sdRegion.getRegionAtCharacterOffset(offset); |
| if (region == null) |
| return null; |
| |
| if (sdRegion.getStartOffset(region) == offset) { |
| // The offset is at the beginning of the region |
| if ((sdRegion.getStartOffset(region) == sdRegion.getStartOffset()) && (sdRegion.getPrevious() != null) && (!sdRegion.getPrevious().isEnded())) { |
| // Is the region also the start of the node? If so, the |
| // previous IStructuredDocumentRegion is |
| // where to look for a useful region. |
| region = sdRegion.getPrevious().getRegionAtCharacterOffset(offset - 1); |
| } |
| else { |
| // Is there no separating whitespace from the previous region? |
| // If not, |
| // then that region is the important one |
| ITextRegion previousRegion = sdRegion.getRegionAtCharacterOffset(offset - 1); |
| if ((previousRegion != null) && (previousRegion != region) && (previousRegion.getTextLength() == previousRegion.getLength())) { |
| region = previousRegion; |
| } |
| } |
| } |
| else { |
| // The offset is NOT at the beginning of the region |
| if (offset > sdRegion.getStartOffset(region) + region.getTextLength()) { |
| // Is the offset within the whitespace after the text in this |
| // region? |
| // If so, use the next region |
| ITextRegion nextRegion = sdRegion.getRegionAtCharacterOffset(sdRegion.getStartOffset(region) + region.getLength()); |
| if (nextRegion != null) |
| region = nextRegion; |
| } |
| else { |
| // Is the offset within the important text for this region? |
| // If so, then we've already got the right one. |
| } |
| } |
| |
| // valid WHITE_SPACE region handler (#179924) |
| if (region != null && region.getType() == DOMRegionContext.WHITE_SPACE) { |
| ITextRegion previousRegion = sdRegion.getRegionAtCharacterOffset(sdRegion.getStartOffset(region) - 1); |
| if (previousRegion != null) |
| region = previousRegion; |
| } |
| |
| return region; |
| } |
| |
| /** |
| * Return the region whose content's require completion. This is something |
| * of a misnomer as sometimes the user wants to be prompted for contents |
| * of a non-existant ITextRegion, such as for enumerated attribute values |
| * following an '=' sign. |
| */ |
| protected ITextRegion getCompletionRegion(int documentPosition, Node domnode) { |
| if (domnode == null) |
| return null; |
| |
| ITextRegion region = null; |
| int offset = documentPosition; |
| IStructuredDocumentRegion flatNode = null; |
| IDOMNode node = (IDOMNode) domnode; |
| |
| if (node.getNodeType() == Node.DOCUMENT_NODE) { |
| if (node.getStructuredDocument().getLength() == 0) |
| return null; |
| ITextRegion result = node.getStructuredDocument().getRegionAtCharacterOffset(offset).getRegionAtCharacterOffset(offset); |
| while (result == null) { |
| offset--; |
| result = node.getStructuredDocument().getRegionAtCharacterOffset(offset).getRegionAtCharacterOffset(offset); |
| } |
| return result; |
| } |
| |
| IStructuredDocumentRegion startTag = node.getStartStructuredDocumentRegion(); |
| IStructuredDocumentRegion endTag = node.getEndStructuredDocumentRegion(); |
| |
| // Determine if the offset is within the start |
| // IStructuredDocumentRegion, end IStructuredDocumentRegion, or |
| // somewhere within the Node's XML content. |
| if ((startTag != null) && (startTag.getStartOffset() <= offset) && (offset < startTag.getStartOffset() + startTag.getLength())) |
| flatNode = startTag; |
| else if ((endTag != null) && (endTag.getStartOffset() <= offset) && (offset < endTag.getStartOffset() + endTag.getLength())) |
| flatNode = endTag; |
| |
| if (flatNode != null) { |
| // the offset is definitely within the start or end tag, continue |
| // on and find the region |
| region = getCompletionRegion(offset, flatNode); |
| } |
| else { |
| // the docPosition is neither within the start nor the end, so it |
| // must be content |
| flatNode = node.getStructuredDocument().getRegionAtCharacterOffset(offset); |
| // (pa) ITextRegion refactor |
| // if (flatNode.contains(documentPosition)) { |
| if (flatNode.getStartOffset() <= documentPosition && flatNode.getEndOffset() >= documentPosition) { |
| // we're interesting in completing/extending the previous |
| // IStructuredDocumentRegion if the current |
| // IStructuredDocumentRegion isn't plain content or if it's |
| // preceded by an orphan '<' |
| if ((offset == flatNode.getStartOffset()) && (flatNode.getPrevious() != null) && (flatNode.getRegionAtCharacterOffset(documentPosition) != null && flatNode.getRegionAtCharacterOffset(documentPosition).getType() != DOMRegionContext.XML_CONTENT || flatNode.getPrevious().getLastRegion().getType() == DOMRegionContext.XML_TAG_OPEN || flatNode.getPrevious().getLastRegion().getType() == DOMRegionContext.XML_END_TAG_OPEN)) { |
| // Is the region also the start of the node? If so, the |
| // previous IStructuredDocumentRegion is |
| // where to look for a useful region. |
| region = flatNode.getPrevious().getLastRegion(); |
| } |
| else if (flatNode.getEndOffset() == documentPosition) { |
| region = flatNode.getLastRegion(); |
| } |
| else |
| region = flatNode.getFirstRegion(); |
| } |
| else { |
| // catch end of document positions where the docPosition isn't |
| // in a IStructuredDocumentRegion |
| region = flatNode.getLastRegion(); |
| } |
| } |
| |
| return region; |
| } |
| |
| /** |
| * Provided by default. Subclasses may override with their own |
| * implementations. |
| * |
| * @see AbstractContentAssistProcessor#getContentGenerator() |
| */ |
| public XMLContentModelGenerator getContentGenerator() { |
| if (fGenerator == null) |
| fGenerator = new XMLContentModelGenerator(); |
| return fGenerator; |
| } |
| |
| /** |
| * Returns the characters which when entered by the user should |
| * automatically trigger the presentation of context information. |
| * |
| * the auto activation characters for presenting context information or |
| * <code>null</code> if no auto activation is desired |
| */ |
| public char[] getContextInformationAutoActivationCharacters() { |
| return contextInformationAutoActivationCharacters; |
| } |
| |
| /** |
| * Returns a validator used to determine when displayed context |
| * information should be dismissed. May only return <code>null</code> if |
| * the processor is incapable of computing context information. |
| * |
| * a context information validator, or <code>null</code> if the |
| * processor is incapable of computing context information |
| */ |
| public IContextInformationValidator getContextInformationValidator() { |
| if (fContextInformationPresenter == null) |
| fContextInformationPresenter = new AttributeContextInformationPresenter(); |
| return fContextInformationPresenter; |
| } |
| |
| protected int getElementPosition(Node child) { |
| Node parent = child.getParentNode(); |
| if (parent == null) |
| return 0; |
| |
| NodeList children = parent.getChildNodes(); |
| if (children == null) |
| return 0; |
| int count = 0; |
| |
| for (int i = 0; i < children.getLength(); i++) { |
| if (children.item(i) == child) |
| return count; |
| else |
| // if (children.item(i).getNodeType() == Node.ELEMENT_NODE) |
| count++; |
| } |
| return 0; |
| } |
| |
| private int getElementPositionForModelQuery(Node child) { |
| return getElementPosition(child); |
| // return -1; |
| } |
| |
| /** |
| * Return the reason why computeProposals was not able to find any |
| * completions. |
| * |
| * an error message or null if no error occurred |
| */ |
| public String getErrorMessage() { |
| return fErrorMessage; |
| } |
| |
| /** |
| * @param iResource |
| */ |
| // public void initialize(IResource iResource) { |
| // this.resource = iResource; |
| // } |
| /** |
| * Gets the infoProvider. |
| * |
| * fInfoProvider and if fInfoProvider was <code>null</code> create a new |
| * instance |
| */ |
| public MarkupTagInfoProvider getInfoProvider() { |
| if (fInfoProvider == null) { |
| fInfoProvider = new MarkupTagInfoProvider(); |
| } |
| return fInfoProvider; |
| } |
| |
| protected String getMatchString(IStructuredDocumentRegion parent, ITextRegion aRegion, int offset) { |
| if (aRegion == null || isCloseRegion(aRegion)) |
| return ""; //$NON-NLS-1$ |
| String matchString = null; |
| String regionType = aRegion.getType(); |
| if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == DOMRegionContext.XML_TAG_OPEN || (offset > parent.getStartOffset(aRegion) + aRegion.getTextLength())) { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| else if (regionType == DOMRegionContext.XML_CONTENT) { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| else { |
| if (parent.getText(aRegion).length() > 0 && parent.getStartOffset(aRegion) < offset) |
| matchString = parent.getText(aRegion).substring(0, offset - parent.getStartOffset(aRegion)); |
| else |
| matchString = ""; //$NON-NLS-1$ |
| } |
| return matchString; |
| } |
| |
| protected ITextRegion getNameRegion(IStructuredDocumentRegion flatNode) { |
| if (flatNode == null) |
| return null; |
| Iterator regionList = flatNode.getRegions().iterator(); |
| while (regionList.hasNext()) { |
| ITextRegion region = (ITextRegion) regionList.next(); |
| if (isNameRegion(region)) |
| return region; |
| } |
| return null; |
| } |
| |
| /** |
| * Retrieves all of the possible valid values for this attribute |
| * declaration |
| */ |
| protected List getPossibleDataTypeValues(Node node, CMAttributeDeclaration ad) { |
| List list = null; |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| Element element = (Element) node; |
| String[] dataTypeValues = null; |
| // The ModelQuery may not be available if the corresponding |
| // adapter |
| // is absent |
| ModelQuery modelQuery = ModelQueryUtil.getModelQuery(element.getOwnerDocument()); |
| if (modelQuery != null) { |
| dataTypeValues = modelQuery.getPossibleDataTypeValues(element, ad); |
| } |
| else { |
| if (ad.getAttrType() != null) |
| dataTypeValues = ad.getAttrType().getEnumeratedValues(); |
| } |
| if (dataTypeValues != null) { |
| list = new ArrayList(dataTypeValues.length); |
| for (int i = 0; i < dataTypeValues.length; i++) { |
| list.add(dataTypeValues[i]); |
| } |
| } |
| } |
| if (list == null) { |
| list = new ArrayList(0); |
| } |
| return list; |
| } |
| |
| protected String getRequiredName(Node parentOrOwner, CMNode cmnode) { |
| if (cmnode == null || parentOrOwner == null) { |
| if (Debug.displayWarnings) { |
| new IllegalArgumentException("Null declaration!").printStackTrace(); //$NON-NLS-1$ |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| return getContentGenerator().getRequiredName(parentOrOwner, cmnode); |
| } |
| |
| protected String getRequiredText(Node parentOrOwner, CMAttributeDeclaration attrDecl) { |
| if (attrDecl == null) { |
| if (Debug.displayWarnings) { |
| new IllegalArgumentException("Null attribute declaration!").printStackTrace(); //$NON-NLS-1$ |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| StringBuffer buff = new StringBuffer(); |
| getContentGenerator().generateRequiredAttribute(parentOrOwner, attrDecl, buff); |
| return buff.toString(); |
| } |
| |
| protected String getRequiredText(Node parentOrOwner, CMElementDeclaration elementDecl) { |
| if (elementDecl == null) { |
| if (Debug.displayWarnings) { |
| new IllegalArgumentException("Null attribute declaration!").printStackTrace(); //$NON-NLS-1$ |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| StringBuffer buff = new StringBuffer(); |
| getContentGenerator().generateTag(parentOrOwner, elementDecl, buff); |
| return buff.toString(); |
| } |
| |
| /** |
| * StructuredTextViewer must be set before using this. |
| */ |
| public IStructuredDocumentRegion getStructuredDocumentRegion(int pos) { |
| // (pa) ITextRegion refactor defect 245190 |
| // return |
| // (IStructuredDocumentRegion)ContentAssistUtils.getNodeAt((StructuredTextViewer)fTextViewer, |
| // pos); |
| return ContentAssistUtils.getStructuredDocumentRegion(fTextViewer, pos); |
| } |
| |
| private String getTagName(IStructuredDocumentRegion sdRegion) { |
| ITextRegionList regions = sdRegion.getRegions(); |
| ITextRegion region = null; |
| String name = ""; //$NON-NLS-1$ |
| for (int i = 0; i < regions.size(); i++) { |
| region = regions.get(i); |
| if (region.getType() == DOMRegionContext.XML_TAG_NAME) { |
| name = sdRegion.getText(region); |
| break; |
| } |
| } |
| return name; |
| } |
| |
| // returns a list of CMNodes that are available within this parent context |
| // Given the grammar shown below and a snippet of XML code (where the '|' indicated the cursor position) |
| // the list would return all of the element declarations that are potential child elements of Foo. |
| // |
| // grammar : Foo -> (A, B, C) |
| // snippet : <Foo><A>| |
| // result : {A, B, C} |
| // |
| // TODO cs... do we need to pass in the 'kindOfAction'? Seems to me we could assume it's always an insert. |
| protected List getAvailableChildElementDeclarations(Element parent, int childPosition, int kindOfAction) |
| { |
| List modelQueryActions = getAvailableChildrenAtIndex(parent, childPosition, ModelQuery.VALIDITY_NONE); |
| Iterator iterator = modelQueryActions.iterator(); |
| List cmnodes = new Vector(); |
| while (iterator.hasNext()) { |
| ModelQueryAction action = (ModelQueryAction) iterator.next(); |
| if (childPosition < 0 || (action.getStartIndex() <= childPosition && childPosition <= action.getEndIndex()) && action.getKind() == kindOfAction) { |
| CMNode actionCMNode = action.getCMNode(); |
| if (actionCMNode != null && !cmnodes.contains(actionCMNode)) |
| cmnodes.add(actionCMNode); |
| } |
| } |
| return cmnodes; |
| } |
| |
| // returns a list of CMNodes that can be validly inserted at this childPosition |
| // Given the grammar shown below and a snippet of XML code (where the '|' indicated the cursor position) |
| // the list would return only the element declarations can be inserted while maintaing validity of the content. |
| // |
| // grammar : Foo -> (A, B, C) |
| // snippet : <Foo><A>| |
| // result : {B} |
| // |
| protected List getValidChildElementDeclarations(Element parent, int childPosition, int kindOfAction) |
| { |
| List modelQueryActions = getAvailableChildrenAtIndex(parent, childPosition, ModelQuery.VALIDITY_STRICT); |
| Iterator iterator = modelQueryActions.iterator(); |
| List cmnodes = new Vector(); |
| while (iterator.hasNext()) { |
| ModelQueryAction action = (ModelQueryAction) iterator.next(); |
| if (childPosition < 0 || (action.getStartIndex() <= childPosition && childPosition <= action.getEndIndex()) && action.getKind() == kindOfAction) { |
| CMNode actionCMNode = action.getCMNode(); |
| if (actionCMNode != null && !cmnodes.contains(actionCMNode)) |
| cmnodes.add(actionCMNode); |
| } |
| } |
| return cmnodes; |
| } |
| |
| /** |
| * Similar to the call in HTMLContentAssistProcessor. Pass in a node, it |
| * tells you if the document is XML type. |
| * |
| * @param node |
| * |
| */ |
| protected boolean getXML(Node node) { |
| if (node == null) |
| return false; |
| |
| Document doc = null; |
| doc = (node.getNodeType() != Node.DOCUMENT_NODE) ? node.getOwnerDocument() : ((Document) node); |
| |
| return (doc instanceof IDOMDocument) && ((IDOMDocument) doc).isXMLType(); |
| } |
| |
| // Initialize local settings |
| protected void init() { |
| // implement in subclasses |
| } |
| |
| protected boolean isCloseRegion(ITextRegion region) { |
| String type = region.getType(); |
| return ((type == DOMRegionContext.XML_PI_CLOSE) || (type == DOMRegionContext.XML_TAG_CLOSE) || (type == DOMRegionContext.XML_EMPTY_TAG_CLOSE) || (type == DOMRegionContext.XML_CDATA_CLOSE) || (type == DOMRegionContext.XML_COMMENT_CLOSE) || (type == DOMRegionContext.XML_ATTLIST_DECL_CLOSE) || (type == DOMRegionContext.XML_ELEMENT_DECL_CLOSE) || (type == DOMRegionContext.XML_DOCTYPE_DECLARATION_CLOSE) || (type == DOMJSPRegionContextsPrivateCopy.JSP_CLOSE) || (type == DOMJSPRegionContextsPrivateCopy.JSP_COMMENT_CLOSE) || (type.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_CLOSE)) || (type == DOMRegionContext.XML_DECLARATION_CLOSE)); |
| } |
| |
| /* |
| * This is to determine if a tag is a special meta-info comment tag that |
| * shows up as an ELEMENT |
| */ |
| private boolean isCommentNode(IDOMNode node) { |
| return (node != null && node instanceof IDOMElement && ((IDOMElement) node).isCommentTag()); |
| } |
| |
| /** |
| * Checks if cursor position is after doctype tag... |
| * |
| * @param car |
| * |
| */ |
| protected boolean isCursorAfterDoctype(ContentAssistRequest car) { |
| Node aNode = car.getNode(); |
| Document parent = aNode.getOwnerDocument(); |
| int xmldoctypeNodePosition = -1; |
| boolean isAfterDoctype = true; |
| |
| if (parent == null) |
| return true; // blank document case |
| |
| for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (child instanceof IDOMNode) { |
| if (child.getNodeType() == Node.DOCUMENT_TYPE_NODE) { |
| xmldoctypeNodePosition = ((IDOMNode) child).getEndOffset(); |
| isAfterDoctype = (car.getReplacementBeginPosition() >= xmldoctypeNodePosition); |
| break; |
| } |
| } |
| } |
| return isAfterDoctype; |
| } |
| |
| /** |
| * This method can check if the cursor is after the XMLPI |
| * |
| * @param car |
| * |
| */ |
| protected boolean isCursorAfterXMLPI(ContentAssistRequest car) { |
| Node aNode = car.getNode(); |
| boolean xmlpiFound = false; |
| Document parent = aNode.getOwnerDocument(); |
| int xmlpiNodePosition = -1; |
| boolean isAfterXMLPI = false; |
| |
| if (parent == null) |
| return true; // blank document case |
| |
| for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
| boolean xmlpi = (child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE && child.getNodeName().equals("xml")); //$NON-NLS-1$ |
| xmlpiFound = xmlpiFound || xmlpi; |
| if (xmlpiFound) { |
| if (child instanceof IDOMNode) { |
| xmlpiNodePosition = ((IDOMNode) child).getEndOffset(); |
| isAfterXMLPI = (car.getReplacementBeginPosition() >= xmlpiNodePosition); |
| } |
| break; |
| } |
| } |
| return isAfterXMLPI; |
| } |
| |
| protected boolean isNameRegion(ITextRegion region) { |
| String type = region.getType(); |
| return ((type == DOMRegionContext.XML_TAG_NAME) || (type == DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_NAME) || (type == DOMRegionContext.XML_ELEMENT_DECL_NAME) || (type == DOMRegionContext.XML_DOCTYPE_NAME) || (type == DOMRegionContext.XML_ATTLIST_DECL_NAME) || (type == DOMJSPRegionContextsPrivateCopy.JSP_ROOT_TAG_NAME) || type.equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_NAME)); |
| } |
| |
| protected boolean isQuote(String string) { |
| String trimmed = string.trim(); |
| if (trimmed.length() > 0) { |
| return trimmed.charAt(0) == '\'' || trimmed.charAt(0) == '"'; |
| } |
| return false; |
| } |
| |
| private boolean isSelfClosed(IStructuredDocumentRegion startTag) { |
| ITextRegionList regions = startTag.getRegions(); |
| return regions.get(regions.size() - 1).getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE; |
| } |
| |
| private boolean isStartTag(IStructuredDocumentRegion sdRegion) { |
| boolean result = false; |
| if (sdRegion.getRegions().size() > 0) { |
| ITextRegion r = sdRegion.getRegions().get(0); |
| result = r.getType() == DOMRegionContext.XML_TAG_OPEN && sdRegion.isEnded(); |
| } |
| return result; |
| } |
| |
| protected Properties mapToProperties(CMNamedNodeMap map) { |
| Properties p = new Properties(); |
| for (int i = 0; i < map.getLength(); i++) { |
| CMEntityDeclaration decl = (CMEntityDeclaration) map.item(i); |
| p.put(decl.getName(), decl.getValue()); |
| } |
| return p; |
| } |
| |
| /** |
| * Gets the corresponding XMLNode, and checks if it's closed. |
| * |
| * @param startTag |
| * |
| */ |
| private boolean needsEndTag(IStructuredDocumentRegion startTag) { |
| boolean result = false; |
| IStructuredModel sModel = StructuredModelManager.getModelManager().getExistingModelForRead(fTextViewer.getDocument()); |
| try { |
| if (sModel != null) { |
| IDOMNode xmlNode = (IDOMNode) sModel.getIndexedRegion(startTag.getStart()); |
| if (!isStartTag(startTag)) |
| result = false; |
| else if (isSelfClosed(startTag)) |
| result = false; |
| else if (!xmlNode.isContainer()) |
| result = false; |
| else |
| result = xmlNode.getEndStructuredDocumentRegion() == null; |
| } |
| } |
| finally { |
| if (sModel != null) |
| sModel.releaseFromRead(); |
| } |
| return result; |
| } |
| |
| protected ContentAssistRequest newContentAssistRequest(Node node, Node possibleParent, IStructuredDocumentRegion documentRegion, ITextRegion completionRegion, int begin, int length, String filter) { |
| return new ContentAssistRequest(node, possibleParent, documentRegion, completionRegion, begin, length, filter); |
| } |
| |
| public void release() { |
| fGenerator = null; |
| } |
| |
| /** |
| * Set the reason why computeProposals was not able to find any |
| * completions. |
| */ |
| public void setErrorMessage(String errorMessage) { |
| fErrorMessage = errorMessage; |
| } |
| |
| /** |
| * Set the reason why computeProposals was not able to find any |
| * completions. |
| */ |
| protected void setErrorMessage(String errorMessage, String append) { |
| setErrorMessage(errorMessage + append); |
| } |
| |
| /** |
| * Set the reason why computeProposals was not able to find any |
| * completions. |
| */ |
| protected void setErrorMessage(String errorMessage, String prepend, String append) { |
| setErrorMessage(prepend + errorMessage + append); |
| } |
| |
| protected boolean stringsEqual(String a, String b) { |
| // (pa) 221190 matching independent of case to be consistant with Java |
| // editor CA |
| return a.equalsIgnoreCase(b); |
| } |
| } |