blob: 7564f14f5d80b2072e5b54e1c23cfa408807e5f7 [file] [log] [blame]
/*******************************************************************************
* 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.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.StructuredModelManager;
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.StructuredTextViewer;
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.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.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() {
super();
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);
// CMVC 246618
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); // CMVC
// 269884
}
// 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)) {
CustomCompletionProposal proposal = new CustomCompletionProposal("\"" + possibleValue + "\"", //$NON-NLS-2$//$NON-NLS-1$
contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), possibleValue.length() + 1, image, possibleValue, 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) {
CustomCompletionProposal proposal = new CustomCompletionProposal("\"" + value + "\"", //$NON-NLS-2$//$NON-NLS-1$
contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), value.length() + 1, image, value, null, proposedInfo, XMLRelevanceConstants.R_XML_ATTRIBUTE_VALUE);
contentAssistRequest.addProposal(proposal);
if (currentValue.length() > 0 && !value.equals(currentValue)) {
proposal = new CustomCompletionProposal("\"" + currentValue + "\"", //$NON-NLS-2$//$NON-NLS-1$
contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), currentValue.length() + 1, image, currentValue, 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) {
proposal = new CustomCompletionProposal("\"" + currentValue + "\"", //$NON-NLS-2$//$NON-NLS-1$
contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), 1, image, "\"" + currentValue + "\"", //$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) {
addXMLProposal(contentAssistRequest);
}
/**
* 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();
// CMVC 241090 for special meta-info comment tags
if (isCommentNode(node)) {
// loop and find non comment node parent
while (node != null && isCommentNode(node)) {
node = (IDOMNode) node.getParentNode();
}
}
// 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) {
// fix for CMVC 261790
// ////////////////////////////////////////////////////////////////////////////////////
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;
}
}
// fix for CMVC 261790
// ////////////////////////////////////////////////////////////////////////////////////
// fix for CMVC 263163, 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;
}
}
// end fix for CMVC 261790
// ////////////////////////////////////////////////////////////////////////////////////
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...
// CMVC 242943
// CMVC 245532
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)) {
}
// check if theres a node inbetween XMLPI and cursor position
if (child != null && child instanceof IDOMNode) {
// CMVC 257486
if (contentAssistRequest.getReplacementBeginPosition() >= ((IDOMNode) child).getEndOffset() || !xmlpiIsFirstElement) {
insertDoctype = false;
}
}
break;
}
}
if (!xmlpiFound) {
addXMLProposal(contentAssistRequest);
}
else if (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) {
// FIX FOR CMVC 247482
// 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();
List validActions = null;
String error = null;
// CMVC #242943 shouldn't have proposals before XMLPI
// (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;
// CMVC 248081
// 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 = (IDOMNode) 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 children
validActions = getAvailableChildrenAtIndex((Element) parent, childPosition);
cmnodes = getValidCMNodes(childPosition, ModelQueryAction.INSERT, validActions);
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);
Image image = CMImageUtil.getImage(elementDecl);
if (image == null) {
image = XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_GENERIC);
}
// Account for the &lt; and &gt;. If attributes were
// added, the cursor will be placed
// at the offset before of the first character of the
// first attribute name.
int markupAdjustment = getContentGenerator().getMinimalStartTagLength(parent, elementDecl);
if (beginsWith(tagname, matchString)) {
String proposedText = getRequiredText(parent, elementDecl);
String proposedInfo = getAdditionalInfo(parentDecl, elementDecl);
CustomCompletionProposal proposal = new CustomCompletionProposal(proposedText, contentAssistRequest.getReplacementBeginPosition(), contentAssistRequest.getReplacementLength(), markupAdjustment, image, tagname, null, proposedInfo, XMLRelevanceConstants.R_TAG_INSERTION);
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 (!xmlpiFound && contentAssistRequest.getReplacementBeginPosition() < minimumOffset) {
addXMLProposal(contentAssistRequest);
}
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 &lt; and &gt;
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();
List validActions = null;
String error = null;
String matchString = contentAssistRequest.getMatchString();
if (parent.getNodeType() == Node.ELEMENT_NODE) {
// retrieve the list of children
validActions = getAvailableChildrenAtIndex((Element) parent, childPosition);
cmnodes = getValidCMNodes(childPosition, ModelQueryAction.INSERT, validActions);
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 {
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;
proposedText = getRequiredName(parent, ed);
if (!beginsWith(proposedText, matchString))
continue;
if ((node != null && node.getAttributes() != null && node.getAttributes().getLength() > 0) || ((node.getNodeType() != Node.TEXT_NODE) && node.getFirstStructuredDocumentRegion().isEnded())) {
cursorAdjustment = proposedText.length();
}
else {
cursorAdjustment = proposedText.length();
if (ed instanceof CMElementDeclaration) {
CMElementDeclaration elementDecl = (CMElementDeclaration) ed;
if (elementDecl.getContentType() == CMElementDeclaration.EMPTY) {
proposedText += getContentGenerator().getStartTagClose(parent, elementDecl);
cursorAdjustment = proposedText.length();
}
else {
cursorAdjustment = proposedText.length() + 1;
proposedText += "></" + getRequiredName(parent, elementDecl) + ">"; //$NON-NLS-2$//$NON-NLS-1$
}
}
}
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);
}
}
}
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) 221190 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((StructuredTextViewer) 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();
}
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) {
contentAssistRequest = newContentAssistRequest(node, node.getParentNode(), sdRegion, completionRegion, sdRegion.getStartOffset(completionRegion), completionRegion.getTextLength(), 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) {
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;
int vc = (editMode == ModelQuery.EDIT_MODE_CONSTRAINED_STRICT) ? ModelQuery.VALIDITY_STRICT : ModelQuery.VALIDITY_NONE;
modelQuery.getInsertActions(parent, parentDecl, index, ic, vc, 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((StructuredTextViewer) 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;
}
protected List getValidCMNodes(int childPosition, int kindOfAction, List modelQueryActions) {
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);
}
}