Package org.eclipse.wst.xml.ui.internal.contentassist

Source Code of org.eclipse.wst.xml.ui.internal.contentassist.AbstractXMLCompletionProposalComputer

/*******************************************************************************
* Copyright (c) 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*    
*******************************************************************************/
package org.eclipse.wst.xml.ui.internal.contentassist;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Vector;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer;
import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer;
import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
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.contentassist.tapestry.TapestryContants;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* <p>
* Implements the framework for making proposals in XML type documents. Deals
* with all the nastiness needed to figure out where in an XML type document the
* content assist was invoked and then calls one of many abstract methods
* depending on the area the content assist was invoked. In this way
* implementers only have to worry about implementing what to do in each
* situation while not having to deal with figuring out which situation the
* content assist was invoked in.
* </p>
*
* @base
*       org.eclipse.wst.xml.ui.internal.contentassist.AbstractContentAssistProcessor
* @see DefaultXMLCompletionProposalComputer
*/
public abstract class AbstractXMLCompletionProposalComputer implements
    ICompletionProposalComputer {
  /**
   * 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$
  }

  private String fErrorMessage;
  private ITextViewer fTextViewer;

  public AbstractXMLCompletionProposalComputer() {
    fErrorMessage = null;
    fTextViewer = null;
  }

  /**
   * <p>
   * 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.
   * </p>
   *
   * @see org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext,
   *      org.eclipse.core.runtime.IProgressMonitor)
   */
  public List computeCompletionProposals(
      CompletionProposalInvocationContext context,
      IProgressMonitor monitor) {
    ITextViewer textViewer = context.getViewer();
    int documentPosition = context.getInvocationOffset();
    setErrorMessage(null);
    fTextViewer = textViewer;
    IndexedRegion treeNode = null;
    try{
      treeNode = ContentAssistUtils.getNodeAt(textViewer, documentPosition);
    }catch(Exception e){}
    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 = null;
        try {
          if (textViewer.getDocument() instanceof IStructuredDocument) {
            sModel = StructuredModelManager.getModelManager()
                .getModelForRead(
                    (IStructuredDocument) textViewer
                        .getDocument());
          }
          if (sModel != null) {
            IDOMDocument docNode = ((IDOMModel) sModel)
                .getDocument();
            contentAssistRequest = new ContentAssistRequest(
                docNode, docNode, sdRegion, completionRegion,
                documentPosition, 0, null);
            addEmptyDocumentProposals(contentAssistRequest, context);
          }
        } finally {
          if (sModel != null) {
            sModel.releaseFromRead();
          }
        }
        if (contentAssistRequest == null) {
          Logger.logException(new IllegalStateException(
              "problem getting model")); //$NON-NLS-1$
          return new ArrayList(0);
        }

        ICompletionProposal[] props = contentAssistRequest
            .getCompletionProposals();
        return (props != null) ? Arrays.asList(props)
            : new ArrayList(0);
      }
      // MASSIVE ERROR CONDITION
      Logger.logException(new IllegalStateException(
          "completion region was null")); //$NON-NLS-1$
      setErrorMessage(XMLUIMessages.SEVERE_internal_error_occu_UI_);
      contentAssistRequest = new ContentAssistRequest((Node) treeNode,
          node.getParentNode(), sdRegion, completionRegion,
          documentPosition, 0, ""); //$NON-NLS-1$
      ICompletionProposal[] props = contentAssistRequest
          .getCompletionProposals();
      return (props != null) ? Arrays.asList(props) : new ArrayList(0);
    }

    // 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(matchString,
          completionRegion, (IDOMNode) treeNode, xmlnode, context);
      ICompletionProposal[] props = contentAssistRequest
          .getCompletionProposals();
      return (props != null) ? Arrays.asList(props) : new ArrayList(0);
    }
   
    if (isTapestryELRequest(matchString, completionRegion, (IDOMNode) treeNode,
        xmlnode, context)) {
      // Compute ${} tapestry class entities auto-complate list
      contentAssistRequest = computeTapestryELProposals(matchString,
          completionRegion, (IDOMNode) treeNode, xmlnode, context);
    } else if (isTapestryELMessageRequest(matchString, completionRegion,
        (IDOMNode) treeNode, xmlnode, context)) {
      // Compute ${message: } tapestry messages auto-complate list
      contentAssistRequest = computeTapestryMessageProposals(matchString,
          completionRegion, (IDOMNode) treeNode, xmlnode, context);
    } else if (isTapestryELPropRequest(matchString, completionRegion,
        (IDOMNode) treeNode, xmlnode, context)) {
      // Compute ${prop: } tapestry messages auto-complate list
      contentAssistRequest = computeTapestryPropProposals(matchString,
          completionRegion, (IDOMNode) treeNode, xmlnode, context);
    } else {
      // compute normal proposals
      contentAssistRequest = computeCompletionProposals(matchString,
          completionRegion, (IDOMNode) treeNode, xmlnode, context);
    }

    if (contentAssistRequest == null) {
      contentAssistRequest = new ContentAssistRequest((Node) treeNode,
          node.getParentNode(), sdRegion, completionRegion,
          documentPosition, 0, ""); //$NON-NLS-1$
      setErrorMessage(XMLUIMessages.Content_Assist_not_availab_UI_);
    }

    /*
     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=123892 Only set this
     * error message if nothing else was already set
     */
    if (contentAssistRequest.getProposals().size() == 0
        && getErrorMessage() == null) {
      setErrorMessage(XMLUIMessages.Content_Assist_not_availab_UI_);
    }

    ICompletionProposal[] props = contentAssistRequest
        .getCompletionProposals();
    return (props != null) ? Arrays.asList(props) : new ArrayList(0);
  }

  /**
   * <p>
   * Returns information about possible contexts based on the specified
   * location within the document that corresponds to the current cursor
   * position within the text viewer.
   * </p>
   *
   * @see org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext,
   *      org.eclipse.core.runtime.IProgressMonitor)
   */
  public List computeContextInformation(
      CompletionProposalInvocationContext context,
      IProgressMonitor monitor) {

    // no default context info
    return Collections.EMPTY_LIST;
  }

  /**
   * @see org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer#getErrorMessage()
   */
  public String getErrorMessage() {
    return fErrorMessage;
  }

  /**
   * Add proposals for attribute names
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addAttributeNameProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add proposals for attribute values
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addAttributeValueProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add comment proposals
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addCommentProposal(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add the proposals for a completely empty document
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addEmptyDocumentProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add the proposals for the name in an end tag
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addEndTagNameProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Prompt for end tags to a non-empty Node that hasn't ended Handles these
   * cases: <br>
   * <tagOpen>| <br>
   * <tagOpen>< |<br>
   * <tagOpen></ |
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addEndTagProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add entity proposals
   *
   * @param contentAssistRequest
   * @param completionRegion
   * @param treeNode
   * @param context
   */
  protected abstract void addEntityProposals(
      ContentAssistRequest contentAssistRequest,
      ITextRegion completionRegion, IDOMNode treeNode,
      CompletionProposalInvocationContext context);

  /**
   * Add Tapestry page class EL auto-complete proposals
   *
   * @param contentAssistRequest
   * @param completionRegion
   * @param treeNode
   * @param context
   */
  protected abstract void addTapestryELProposals(
      ContentAssistRequest contentAssistRequest,
      ITextRegion completionRegion, IDOMNode treeNode,
      CompletionProposalInvocationContext context);

  /**
   * Add Tapestry page class messages auto-complete proposals
   *
   * @param contentAssistRequest
   * @param completionRegion
   * @param treeNode
   * @param context
   */
  protected abstract void addTapestryELMessagesProposals(
      ContentAssistRequest contentAssistRequest,
      ITextRegion completionRegion, IDOMNode treeNode,
      CompletionProposalInvocationContext context);
 
  /**
   * Add Tapestry page class property auto-complete proposals
   *
   * @param contentAssistRequest
   * @param completionRegion
   * @param treeNode
   * @param context
   */
  protected abstract void addTapestryELPropProposals(
      ContentAssistRequest contentAssistRequest,
      ITextRegion completionRegion, IDOMNode treeNode,
      CompletionProposalInvocationContext context);

  /**
   * add entity proposals
   *
   * @param proposals
   * @param map
   * @param key
   * @param nodeOffset
   * @param sdRegion
   * @param completionRegion
   * @param context
   */
  protected abstract void addEntityProposals(Vector proposals,
      Properties map, String key, int nodeOffset,
      IStructuredDocumentRegion sdRegion, ITextRegion completionRegion,
      CompletionProposalInvocationContext context);

  /**
   * Add PCData proposals
   *
   * @param nodeName
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addPCDATAProposal(String nodeName,
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add start document proposals
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addStartDocumentProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Close an unclosed start tag
   *
   * @param contentAssistRequest
   * @param context
   */
  protected abstract void addTagCloseProposals(
      ContentAssistRequest contentAssistRequest,
      CompletionProposalInvocationContext context);

  /**
   * Add tag insertion proposals
   *
   * @param contentAssistRequest
   * @param childPosition
   * @param context
   */
  protected abstract void addTagInsertionProposals(
      ContentAssistRequest contentAssistRequest, int childPosition,
      CompletionProposalInvocationContext context);

  /**
   * Add tag name proposals
   *
   * @param contentAssistRequest
   * @param childPosition
   * @param context
   */
  protected abstract void addTagNameProposals(
      ContentAssistRequest contentAssistRequest, int childPosition,
      CompletionProposalInvocationContext context);

  /**
   * @param errorMessage
   *            the reason why computeProposals was not able to find any
   *            completions.
   */
  protected void setErrorMessage(String errorMessage) {
    fErrorMessage = errorMessage;
  }

  /**
   * <p>
   * This does all the magic of figuring out where in the XML type document
   * the content assist was invoked and then calling the corresponding method
   * to add the correct proposals
   * </p>
   *
   * <p>
   * <b>NOTE: </b>if overriding be sure to make super call back to this method
   * otherwise you will loose all of the proposals generated by this method
   * </p>
   *
   * @param matchString
   * @param completionRegion
   * @param treeNode
   * @param xmlnode
   * @param context
   *
   * @return {@link ContentAssistRequest} that now has all the proposals in it
   */
  protected ContentAssistRequest computeCompletionProposals(
      String matchString, ITextRegion completionRegion,
      IDOMNode treeNode, IDOMNode xmlnode,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();

    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(matchString,
            completionRegion, treeNode, xmlnode, context);
      } else if (regionType == DOMRegionContext.XML_TAG_NAME) {
        contentAssistRequest = computeTagNameProposals(matchString,
            completionRegion, treeNode, xmlnode, context);
      } else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
        contentAssistRequest = computeAttributeProposals(matchString,
            completionRegion, treeNode, xmlnode, context);
      } else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
        contentAssistRequest = computeEqualsProposals(matchString,
            completionRegion, treeNode, xmlnode, context);
      } 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 = new ContentAssistRequest(actualNode,
            actualNode, sdRegion, completionRegion,
            documentPosition, 0, matchString);
        addTagCloseProposals(contentAssistRequest, context);
      } else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
        contentAssistRequest = computeAttributeValueProposals(
            matchString, completionRegion, treeNode, xmlnode,
            context);
      } else if ((regionType == DOMRegionContext.XML_TAG_CLOSE)
          || (regionType == DOMRegionContext.XML_EMPTY_TAG_CLOSE)
          || (regionType
              .equals(DOMJSPRegionContextsPrivateCopy.JSP_DIRECTIVE_CLOSE))) {

        contentAssistRequest = computeTagCloseProposals(matchString,
            completionRegion, treeNode, xmlnode, context);
      } else if (regionType == DOMRegionContext.XML_END_TAG_OPEN) {
        contentAssistRequest = computeEndTagOpenProposals(matchString,
            completionRegion, treeNode, xmlnode, context);
      } else if ((regionType == DOMRegionContext.XML_CONTENT)
          || (regionType == DOMRegionContext.XML_CHAR_REFERENCE)
          || (regionType == DOMRegionContext.XML_ENTITY_REFERENCE)
          || (regionType == DOMRegionContext.XML_PE_REFERENCE)) {
        contentAssistRequest = computeContentProposals(matchString,
            completionRegion, treeNode, xmlnode, context);
      }

      // 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 = new ContentAssistRequest(treeNode,
            xmlnode.getParentNode(), sdRegion, completionRegion,
            documentPosition, 0, matchString);
        addTagInsertionProposals(contentAssistRequest,
            getElementPosition(treeNode), context);
        addStartDocumentProposals(contentAssistRequest, context);
      }
    }
    // Not a Document or Element? (odd cases go here for now)
    else if (isCloseRegion(completionRegion)) {
      contentAssistRequest = new ContentAssistRequest(treeNode,
          xmlnode.getParentNode(), sdRegion, completionRegion,
          sdRegion.getStartOffset(completionRegion)
              + completionRegion.getLength(), 0, matchString);
      addStartDocumentProposals(contentAssistRequest, context);
      if (documentPosition >= sdRegion.getTextEndOffset(completionRegion)) {
        addTagInsertionProposals(contentAssistRequest,
            getElementPosition(treeNode) + 1, context);
      }
    } 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 = new ContentAssistRequest(treeNode,
          xmlnode.getParentNode(), sdRegion, completionRegion,
          documentPosition, 0, matchString);
      addTagInsertionProposals(contentAssistRequest,
          getElementPosition(treeNode), context);
      addStartDocumentProposals(contentAssistRequest, context);
    }
    return contentAssistRequest;
  }

  /**
   * <p>
   * Similar to
   * {@link #computeCompletionProposals(CompletionProposalInvocationContext, IProgressMonitor)}
   * only specificly for attribute proposals
   * </p>
   *
   * <p>
   * Implementers should not override this method, it is made available to
   * implementers so that if they override
   * {@link #computeCompletionProposals(String, ITextRegion, IDOMNode, IDOMNode, CompletionProposalInvocationContext)}
   * they can call this method if needed
   * </p>
   *
   * @param matchString
   * @param completionRegion
   * @param nodeAtOffset
   * @param node
   * @param context
   * @return
   */
  protected final ContentAssistRequest computeAttributeProposals(
      String matchString, ITextRegion completionRegion,
      IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    ITextViewer viewer = context.getViewer();
    ContentAssistRequest contentAssistRequest = null;
    IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition);
    // if the attribute name is selected, replace it instead of creating a
    // new attribute
    if (documentPosition <= sdRegion.getStartOffset(completionRegion)
        && (viewer != null && viewer.getSelectedRange().y != (sdRegion
            .getEndOffset(completionRegion) - sdRegion
            .getStartOffset(completionRegion)))) {
      // setup to insert new attributes
      contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node,
          sdRegion, completionRegion, documentPosition, 0,
          matchString);
    } else {
      // Setup to replace an existing attribute name
      contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node,
          sdRegion, completionRegion,
          sdRegion.getStartOffset(completionRegion),
          completionRegion.getTextLength(), matchString);
    }
    addAttributeNameProposals(contentAssistRequest, context);
    contentAssistRequest.setReplacementBeginPosition(documentPosition);
    contentAssistRequest.setReplacementLength(0);
    if ((node.getFirstStructuredDocumentRegion() != null)
        && (!node.getFirstStructuredDocumentRegion().isEnded())) {
      addTagCloseProposals(contentAssistRequest, context);
    }
    return contentAssistRequest;
  }

  /**
   * <p>
   * this is the position the cursor should be in after the proposal is
   * applied
   * </p>
   *
   * @param proposedText
   * @return the position the cursor should be in after the proposal is
   *         applied
   */
  protected static int getCursorPositionForProposedText(String proposedText) {
    int cursorAdjustment;
    cursorAdjustment = proposedText.indexOf("\"\"") + 1; //$NON-NLS-1$
    // otherwise, after the first tag
    if (cursorAdjustment == 0) {
      cursorAdjustment = proposedText.indexOf('>') + 1;
    }
    if (cursorAdjustment == 0) {
      cursorAdjustment = proposedText.length() + 1;
    }

    return cursorAdjustment;
  }

  /**
   * <p>
   * helpful utility method for determining if one string starts with another
   * one. This is case insensitive. If either are null then result is
   * <code>true</code>
   * </p>
   *
   * @param aString
   *            the string to check to see if it starts with the given prefix
   * @param prefix
   *            check that the given string starts with this prefix
   *
   * @return <code>true</code> if the given string starts with the given
   *         prefix, <code>false</code> otherwise
   */
  protected static boolean beginsWith(String aString, String prefix) {
    if ((aString == null) || (prefix == null)) {
      return true;
    }
    return aString.toLowerCase().startsWith(prefix.toLowerCase());
  }

  private ContentAssistRequest computeAttributeValueProposals(
      String matchString, ITextRegion completionRegion,
      IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();

    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 = new ContentAssistRequest(actualNode,
          actualNode, sdRegion, completionRegion, documentPosition,
          0, matchString);
      addAttributeNameProposals(contentAssistRequest, context);
      if ((actualNode.getFirstStructuredDocumentRegion() != null)
          && !actualNode.getFirstStructuredDocumentRegion().isEnded()) {
        addTagCloseProposals(contentAssistRequest, context);
      }
    } 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 = new ContentAssistRequest(nodeAtOffset,
            node, sdRegion, completionRegion, documentPosition, 0,
            matchString);
        addAttributeNameProposals(contentAssistRequest, context);
      } else {
        int replaceLength = completionRegion.getTextLength();

        // if container region, be sure replace length is only the
        // attribute value region not the entire container
        if (completionRegion instanceof ITextRegionContainer) {
          ITextRegion openRegion = ((ITextRegionContainer) completionRegion)
              .getFirstRegion();
          ITextRegion closeRegion = ((ITextRegionContainer) completionRegion)
              .getLastRegion();

          /*
           * check to see if the container is opened the same way its
           * closed. Such as: <img src=' ' But not: <img src='
           *
           * </body> </html> In the latter case we only want to
           * replace the opening text of the container Admittedly
           * crude test, but effective.
           */
          if (openRegion.getType() != closeRegion.getType()) {
            replaceLength = openRegion.getTextLength();
          }
        }

        contentAssistRequest = new ContentAssistRequest(nodeAtOffset,
            node, sdRegion, completionRegion,
            sdRegion.getStartOffset(completionRegion),
            replaceLength, matchString);

        addAttributeValueProposals(contentAssistRequest, context);
      }
    }
    return contentAssistRequest;
  }

  private boolean isTapestryELRequest(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {
    int documentPosition = context.getInvocationOffset();
    char preChar = 0, preChar2 = 0;
    for (int i = documentPosition - node.getStartOffset() - 1; i >= 0; i--) {
      char temp = node.getSource().charAt(i);
      if (temp == 10)
        break;
      else if (temp != 9 && temp != 10 && temp != 32) {
        if (preChar == 0)
          preChar = temp;
        else {
          preChar2 = temp;
          break;
        }
      }
    }
    if (preChar == '{' && preChar2 == '$')
      return true;
    else
      return false;
  }

  private boolean isTapestryELMessageRequest(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {
    int documentPosition = context.getInvocationOffset();
    for (int i = documentPosition - node.getStartOffset() - 1; i >= 0; i--) {
      char temp = node.getSource().charAt(i);
      if (temp == 10)
        break;
      else if (temp == '{') {
        if (i - 1 >= 0 && node.getSource().charAt(i - 1) == '$'
            && i + 1 < documentPosition - node.getStartOffset()) {
          String prefix = node.getSource().substring(i + 1,
              documentPosition - node.getStartOffset());
          if (prefix.trim().equals(TapestryContants.PREFIX_MESSAGE))
            return true;
          else
            break;
        } else
          break;
      }
    }
    return false;
  }
 
  private boolean isTapestryELPropRequest(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {
    int documentPosition = context.getInvocationOffset();
    for (int i = documentPosition - node.getStartOffset() - 1; i >= 0; i--) {
      char temp = node.getSource().charAt(i);
      if (temp == 10)
        break;
      else if (temp == '{') {
        if (i - 1 >= 0 && node.getSource().charAt(i - 1) == '$'
            && i + 1 < documentPosition - node.getStartOffset()) {
          String prefix = node.getSource().substring(i + 1,
              documentPosition - node.getStartOffset());
          if (prefix.trim().equals(TapestryContants.PREFIX_PROP))
            return true;
          else
            break;
        } else
          break;
      }
    }
    return false;
  }

  private ContentAssistRequest computeTapestryMessageProposals(
      String matchString, ITextRegion completionRegion,
      IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {
    int documentPosition = context.getInvocationOffset();
    ContentAssistRequest contentAssistRequest = new ContentAssistRequest(
        nodeAtOffset, node,
        getStructuredDocumentRegion(documentPosition),
        completionRegion, documentPosition, 0, matchString);
    addTapestryELMessagesProposals(contentAssistRequest, completionRegion,
        node, context);
    return contentAssistRequest;
  }
 
  private ContentAssistRequest computeTapestryPropProposals(
      String matchString, ITextRegion completionRegion,
      IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {
    int documentPosition = context.getInvocationOffset();
    ContentAssistRequest contentAssistRequest = new ContentAssistRequest(
        nodeAtOffset, node,
        getStructuredDocumentRegion(documentPosition),
        completionRegion, documentPosition, 0, matchString);
    addTapestryELPropProposals(contentAssistRequest, completionRegion,
        node, context);
    return contentAssistRequest;
  }

  private ContentAssistRequest computeTapestryELProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {
    int documentPosition = context.getInvocationOffset();
    ContentAssistRequest contentAssistRequest = new ContentAssistRequest(
        nodeAtOffset, node,
        getStructuredDocumentRegion(documentPosition),
        completionRegion, documentPosition, 0, matchString);
    addTapestryELProposals(contentAssistRequest, completionRegion, node,
        context);
    return contentAssistRequest;
  }

  private ContentAssistRequest computeContentProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    ContentAssistRequest contentAssistRequest = null;

    // setup to add children at the content node's position
    contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node,
        getStructuredDocumentRegion(documentPosition),
        completionRegion, documentPosition, 0, matchString);

    if ((node != null) && (node.getNodeType() == Node.DOCUMENT_NODE)
        && (((Document) node).getDoctype() == null)) {
      addStartDocumentProposals(contentAssistRequest, context);
    }
    addTagInsertionProposals(contentAssistRequest,
        getElementPosition(nodeAtOffset), context);
    if (node.getNodeType() != Node.DOCUMENT_NODE) {
      addEndTagProposals(contentAssistRequest, context);
    }
    // entities?
    addEntityProposals(contentAssistRequest, completionRegion, node,
        context);
    // addEntityProposals(contentAssistRequest);

    return contentAssistRequest;
  }

  private ContentAssistRequest computeEndTagOpenProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    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 = new ContentAssistRequest(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 = new ContentAssistRequest(nodeAtOffset,
            nodeAtOffset.getParentNode(), sdRegion,
            completionRegion, documentPosition, 0, matchString);
      } else {
        // normal case, this end tag is the parent
        contentAssistRequest = new ContentAssistRequest(nodeAtOffset,
            nodeAtOffset, sdRegion, completionRegion,
            documentPosition, 0, matchString);
      }
    }
    // if (documentPosition >= sdRegion.getStartOffset(completionRegion) +
    // completionRegion.getTextLength())
    addEndTagProposals(contentAssistRequest, context);
    // else
    if (completionRegionStart == documentPosition) {
      // positioned at start of end tag
      addTagInsertionProposals(contentAssistRequest, node.getChildNodes()
          .getLength(), context);
    }
    return contentAssistRequest;
  }

  private ContentAssistRequest computeEqualsProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    ContentAssistRequest contentAssistRequest = null;
    IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition);
    System.out.println(sdRegion.toString());
    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 = new ContentAssistRequest(nodeAtOffset, node,
          sdRegion, valueRegion,
          sdRegion.getStartOffset(valueRegion),
          valueRegion.getTextLength(), matchString);
    } else {
      // append an attribute value after the '='
      contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node,
          sdRegion, completionRegion, documentPosition, 0,
          matchString);
    }
    addAttributeValueProposals(contentAssistRequest, context);
    return contentAssistRequest;
  }

  private ContentAssistRequest computeStartDocumentProposals(
      String matchString, ITextRegion completionRegion,
      IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();

    // setup for a non-empty document, but one that hasn't been formally
    // started
    ContentAssistRequest contentAssistRequest = null;
    contentAssistRequest = new ContentAssistRequest(nodeAtOffset, node,
        getStructuredDocumentRegion(documentPosition),
        completionRegion, documentPosition, 0, matchString);
    addStartDocumentProposals(contentAssistRequest, context);
    return contentAssistRequest;
  }

  private ContentAssistRequest computeTagCloseProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    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 = new ContentAssistRequest(nodeAtOffset, node,
          sdRegion, completionRegion, documentPosition, 0,
          matchString);
      addTagInsertionProposals(contentAssistRequest,
          getElementPosition(nodeAtOffset), context);
      if ((node.getNodeType() != Node.DOCUMENT_NODE)
          && (node.getEndStructuredDocumentRegion() == null)) {
        addEndTagProposals(contentAssistRequest, context);
      }
    } 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 = new ContentAssistRequest(nodeAtOffset,
            node, sdRegion, completionRegion, documentPosition, 0,
            matchString);
        if ((node.getNodeType() != Node.DOCUMENT_NODE)
            && (node.getEndStructuredDocumentRegion() != null)) {
          addTagCloseProposals(contentAssistRequest, context);
        }
        if (sdRegion == nodeAtOffset.getFirstStructuredDocumentRegion()) {
          contentAssistRequest
              .setReplacementBeginPosition(documentPosition);
          contentAssistRequest.setReplacementLength(0);
          addAttributeNameProposals(contentAssistRequest, context);
        }
      }
    }
    return contentAssistRequest;
  }

  private ContentAssistRequest computeTagNameProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    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 = new ContentAssistRequest(
                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,
                  context);
            }
            addTagCloseProposals(contentAssistRequest, context);
          } else {
            // it's name
            contentAssistRequest = new ContentAssistRequest(
                actualNode, actualNode.getParentNode(),
                sdRegion, completionRegion, documentPosition
                    - matchString.length(),
                matchString.length(), matchString);
            addTagNameProposals(contentAssistRequest,
                getElementPosition(actualNode), context);
          }
        } else {
          if (documentPosition >= sdRegion
              .getStartOffset(completionRegion)
              + completionRegion.getLength()) {
            // insert name
            contentAssistRequest = new ContentAssistRequest(
                actualNode, actualNode.getParentNode(),
                sdRegion, completionRegion, documentPosition,
                0, matchString);
          } else {
            // replace name
            contentAssistRequest = new ContentAssistRequest(
                actualNode, actualNode.getParentNode(),
                sdRegion, completionRegion,
                sdRegion.getStartOffset(completionRegion),
                completionRegion.getTextLength(), matchString);
          }
          addEndTagNameProposals(contentAssistRequest, context);
        }
      }
    } else {
      if (documentPosition > sdRegion.getStartOffset(completionRegion)
          + completionRegion.getTextLength()) {
        // unclosed tag with only a name; should prompt for attributes
        // and a close instead
        contentAssistRequest = new ContentAssistRequest(nodeAtOffset,
            node, sdRegion, completionRegion, documentPosition
                - matchString.length(), matchString.length(),
            matchString);
        addAttributeNameProposals(contentAssistRequest, context);
        addTagCloseProposals(contentAssistRequest, context);
      } else {
        if (sdRegion.getRegions().get(0).getType() != DOMRegionContext.XML_END_TAG_OPEN) {
          int replaceLength = documentPosition
              - sdRegion.getStartOffset(completionRegion);
          contentAssistRequest = new ContentAssistRequest(node,
              node.getParentNode(), sdRegion, completionRegion,
              sdRegion.getStartOffset(completionRegion),
              replaceLength, matchString);
          addTagNameProposals(contentAssistRequest,
              getElementPosition(nodeAtOffset), context);
        } else {
          IDOMNode actualNode = (IDOMNode) node.getModel()
              .getIndexedRegion(documentPosition);
          if (actualNode != null) {
            if (documentPosition >= sdRegion
                .getStartOffset(completionRegion)
                + completionRegion.getTextLength()) {
              contentAssistRequest = new ContentAssistRequest(
                  actualNode, actualNode.getParentNode(),
                  sdRegion, completionRegion,
                  documentPosition, 0, matchString);
            } else {
              contentAssistRequest = new ContentAssistRequest(
                  actualNode, actualNode.getParentNode(),
                  sdRegion, completionRegion,
                  sdRegion.getStartOffset(completionRegion),
                  completionRegion.getTextLength(),
                  matchString);
            }
            addEndTagNameProposals(contentAssistRequest, context);
          }
        }
      }
    }
    return contentAssistRequest;
  }

  private ContentAssistRequest computeTagOpenProposals(String matchString,
      ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node,
      CompletionProposalInvocationContext context) {

    int documentPosition = context.getInvocationOffset();
    ContentAssistRequest contentAssistRequest = null;
    IStructuredDocumentRegion sdRegion = getStructuredDocumentRegion(documentPosition);
    if (sdRegion != nodeAtOffset.getFirstStructuredDocumentRegion()
        || sdRegion.getPrevious() != null
        && sdRegion.getPrevious().getLastRegion().getType() == DOMRegionContext.XML_TAG_OPEN) {
      // completing the *first* XML_TAG_OPEN in "<<tagname"
      IDOMNode actualNode = (IDOMNode) node.getModel().getIndexedRegion(
          sdRegion.getStartOffset(completionRegion));
      if (actualNode != null) {
        if (sdRegion.getFirstRegion().getType() == DOMRegionContext.XML_END_TAG_OPEN) {
          contentAssistRequest = new ContentAssistRequest(actualNode,
              actualNode, sdRegion, completionRegion,
              documentPosition, 0, matchString);
          if (actualNode.hasChildNodes())
            addTagNameProposals(contentAssistRequest,
                getElementPosition(actualNode.getLastChild()),
                context);
          else
            addTagNameProposals(contentAssistRequest, 0, context);
        } else {
          contentAssistRequest = new ContentAssistRequest(actualNode,
              actualNode.getParentNode(), sdRegion,
              completionRegion, documentPosition, 0, matchString);
          addTagNameProposals(contentAssistRequest,
              getElementPosition(actualNode), context);
        }
        addEndTagProposals(contentAssistRequest, context); // (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 = new ContentAssistRequest(
              nodeAtOffset, node.getParentNode(), sdRegion,
              completionRegion, documentPosition, 0, matchString);
          addTagInsertionProposals(contentAssistRequest,
              getElementPosition(nodeAtOffset), context);
          addEndTagProposals(contentAssistRequest, context);
        } else if (node.getNodeType() == Node.DOCUMENT_NODE) {
          // at the opening of the VERY first tag with a '<'
          contentAssistRequest = new ContentAssistRequest(
              nodeAtOffset, node.getParentNode(), sdRegion,
              completionRegion,
              sdRegion.getStartOffset(completionRegion),
              completionRegion.getTextLength(), matchString);
          addStartDocumentProposals(contentAssistRequest, context);
        }
      } 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))
            && (sdRegion.getLastRegion().getType() == DOMRegionContext.XML_TAG_CLOSE || sdRegion
                .getLastRegion().getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE)) {

          // replace the existing name
          contentAssistRequest = new ContentAssistRequest(node,
              node.getParentNode(), sdRegion, completionRegion,
              sdRegion.getStartOffset(name),
              name.getTextLength(), matchString);
        } else {
          // insert a valid new name, or possibly an end tag
          contentAssistRequest = new ContentAssistRequest(
              nodeAtOffset,
              ((Node) nodeAtOffset).getParentNode(), sdRegion,
              completionRegion, documentPosition, 0, matchString);
          addEndTagProposals(contentAssistRequest, context);
          contentAssistRequest
              .setReplacementBeginPosition(documentPosition);
          contentAssistRequest.setReplacementLength(0);
        }
        addTagNameProposals(contentAssistRequest,
            getElementPosition(nodeAtOffset), context);
      }
    }
    return contentAssistRequest;
  }

  private 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.
   */
  private 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 {
      // Start test code added by Gavin Lei
      System.out
          .println("Build ITextRegion instance with char '< or $' start    node instance:"
              + node.getClass());
      IStructuredDocumentRegion preFlatNode = node
          .getStructuredDocument().getRegionAtCharacterOffset(
              offset - 1);
      System.out.println(node.getStructuredDocument().getClass());
      System.out.println(preFlatNode.getPrevious().toString());

      // 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;
  }

  private 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 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;
  }

  private 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;
  }

  private 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));
  }

  private 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));
  }

  /**
   * StructuredTextViewer must be set before using this.
   */
  private IStructuredDocumentRegion getStructuredDocumentRegion(int pos) {
    return ContentAssistUtils.getStructuredDocumentRegion(fTextViewer, pos);
  }
}
TOP

Related Classes of org.eclipse.wst.xml.ui.internal.contentassist.AbstractXMLCompletionProposalComputer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.