Package org.eclipse.jst.jsf.core.internal.contentassist.el

Source Code of org.eclipse.jst.jsf.core.internal.contentassist.el.ContentAssistParser$PrefixVisitor

/*******************************************************************************
* Copyright (c) 2006 Oracle Corporation.
* 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:
*    Cameron Bateman/Oracle - initial API and implementation
*   
********************************************************************************/

package org.eclipse.jst.jsf.core.internal.contentassist.el;

import org.eclipse.jface.text.Region;
import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext;
import org.eclipse.jst.jsf.context.symbol.ISymbol;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTAddExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTAndExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTChoiceExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTEqualityExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTFunctionInvocation;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTLiteral;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTMultiplyExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTOrExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTRelationalExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTUnaryExpression;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTValue;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTValuePrefix;
import org.eclipse.jst.jsp.core.internal.java.jspel.ASTValueSuffix;
import org.eclipse.jst.jsp.core.internal.java.jspel.JSPELParser;
import org.eclipse.jst.jsp.core.internal.java.jspel.JSPELParserConstants;
import org.eclipse.jst.jsp.core.internal.java.jspel.JSPELParserVisitor;
import org.eclipse.jst.jsp.core.internal.java.jspel.ParseException;
import org.eclipse.jst.jsp.core.internal.java.jspel.SimpleNode;
import org.eclipse.jst.jsp.core.internal.java.jspel.Token;
import org.eclipse.jst.jsp.core.internal.java.jspel.TokenMgrError;

/**
* Consumes an EL expression and converts into a completion prefix
*
* @author cbateman
*
*/
public final class ContentAssistParser
{
    /**
     * @param relativePosition -- 1-based position in elText (first position is 1)
     * @param elText
     * @return a content assist strategy for the given position and el expression
     * or null if one cannot be determined
     */
    public static ContentAssistStrategy getPrefix(final int relativePosition, final String elText)
    {
        if (elText == null)
        {
            return null;
        }
        else if ("".equals(elText.trim())) //$NON-NLS-1$
        {
            return new IdCompletionStrategy("", "")//$NON-NLS-1$//$NON-NLS-2$
        }
       
        PrefixVisitor visitor = getVisitorForPosition(relativePosition, elText);
        return visitor != null? visitor.getPrefix() : null;
    }
   
    /**
     * Get symbol and symbol region at given position in el string
     * @param context - IStructuredDocumentContext
     * @param relativePosition - position in el string
     * @param elText - el string
     * @return SymbolInfo. May be null.
     */
    public static SymbolInfo getSymbolInfo(IStructuredDocumentContext context, final int relativePosition, final String elText) {
        if (elText == null || "".equals(elText.trim())) //$NON-NLS-1$
        {
            return null;
        }
        PrefixVisitor visitor = getVisitorForPosition(relativePosition, elText);
        if (visitor != null) {
            SymbolInfo symbolInfo = visitor.getSymbolInfo(context);
            if (symbolInfo != null) {
                Region r = symbolInfo.getRelativeRegion();
                if (relativePosition > r.getOffset() && relativePosition <= r.getOffset() + r.getLength()) {
                    return symbolInfo;
                }
            }
        }
        return null;
    }

  private static PrefixVisitor getVisitorForPosition(final int relativePosition,
      final String elText) {
    final java.io.StringReader reader = new java.io.StringReader(elText);
        final JSPELParser  parser = new JSPELParser(reader);
       
        try
        {
            final ASTExpression expr = parser.Expression();
            final PrefixVisitor visitor = new PrefixVisitor(relativePosition, elText);
            expr.jjtAccept(visitor, null);
            return visitor;
        }
        catch (ParseException pe)
        {
            // TODO: handle parser by using current and expected tokens
          return null;
        }
        catch (TokenMgrError tme)
        {
            // TODO: handle parser by using current and expected tokens
          return null;
        }
  }
   
    private static String substring(String s, Region r) {
        return s.substring(r.getOffset(), r.getOffset() + r.getLength());
    }
   
    private static class PrefixVisitor implements JSPELParserVisitor
    {
        private final int       _relativePos;
        private final String    _fullText;
       
        private String          _symbolPrefix; // = null; initialized as tree is visited
        private int             _prefixType;
        private boolean         _prefixResolved;  // = false; set to true when the prefix is resolved
        private int             _symbolStartPos = 1; // first char has position 1
        private int             _symbolEndPos = 0;
       
        PrefixVisitor(final int relativePos, final String fullText)
        {
            _relativePos = relativePos;
            _fullText = fullText;
        }
       
        /**
         * @return the prefix if resolved or null if not resolved
         */
        public ContentAssistStrategy getPrefix()
        {
            if (_prefixResolved)
            {
                switch(_prefixType)
                {
                    case ContentAssistStrategy.PREFIX_TYPE_DOT_COMPLETION:
                        return new FunctionCompletionStrategy(_symbolPrefix, getProposalStart());
                   
                    case ContentAssistStrategy.PREFIX_TYPE_ID_COMPLETION:
                        return new IdCompletionStrategy(_symbolPrefix, getProposalStart());
                   
                    case ContentAssistStrategy.PREFIX_TYPE_EMPTY_EXPRESSION:
                        return new IdCompletionStrategy("", getProposalStart()); //$NON-NLS-1$
                       
                    default:
                        // do nothing; fall-through to return null
                }
            }

            return null;
        }
       
        /**
         * @param context - IStructuredDocumentContext
         * @return symbol and symbol region if resolved, null otherwise
         */
        public SymbolInfo getSymbolInfo(IStructuredDocumentContext context) {
          if (_prefixResolved && _symbolStartPos < _symbolEndPos) {
            Region region = new Region(_symbolStartPos - 1, _symbolEndPos - _symbolStartPos + 1);
                ISymbol symbol = null;
                switch (_prefixType) {
                case ContentAssistStrategy.PREFIX_TYPE_ID_COMPLETION:
                    symbol = SymbolResolveUtil.getSymbolForVariable(context, substring(_fullText, region));
                    break;
                case ContentAssistStrategy.PREFIX_TYPE_DOT_COMPLETION:
                    symbol = SymbolResolveUtil.getSymbolForVariableSuffixExpr(context, _symbolPrefix + "." + substring(_fullText, region), _symbolEndPos == _fullText.length()); //$NON-NLS-1$
                    break;
                }
                if (symbol != null) {
                    return new SymbolInfo(symbol, region);
                }
          }
          return null;
        }

    private String getProposalStart() {
            if (_symbolStartPos <= _relativePos) {
                return _fullText.substring(_symbolStartPos - 1, _relativePos - 1);
            }
            return ""; //$NON-NLS-1$
    }
       
        public Object visit(ASTAddExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

    public Object visit(ASTAndExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTChoiceExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTEqualityExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTFunctionInvocation node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTLiteral node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTMultiplyExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTOrExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTRelationalExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTUnaryExpression node, Object data)
        {
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTValue node, Object data)
        {
            // we're only in this value expr if it contains the cursor
            if (testContainsCursor(node))
            {
                return node.childrenAccept(this, data);
            }
           
            return null;
        }

        public Object visit(ASTValuePrefix node, Object data)
        {
            // for now, only concern ourselves with simple (identifier) prefixes
            if (!_prefixResolved
                    && node.jjtGetNumChildren() == 0
                    && node.getFirstToken().kind == JSPELParserConstants.IDENTIFIER)
            {
                _symbolPrefix = node.getFirstToken().image;
               
                if (testContainsCursor(node))
                {
                    // if the cursor is on this id, we don't need to visit
                    // further since we know both the prefix -- the id -- and
                    // the type -- it's an id completion
                    _prefixType = ContentAssistStrategy.PREFIX_TYPE_ID_COMPLETION;
                    _symbolStartPos = node.getFirstToken().beginColumn;
                    _symbolEndPos = node.getFirstToken().endColumn;
                    _prefixResolved = true;
                }
            }
            return node.childrenAccept(this, data);
        }

        public Object visit(ASTValueSuffix node, Object data)
        {
            // for now, only deal with the simple .id suffix
            Token lastToken = node.getLastToken();
      if (node.jjtGetNumChildren() == 0)
            {
                if (!_prefixResolved
                      && node.getFirstToken().kind == JSPELParserConstants.DOT)
                {
                    if (lastToken.kind == JSPELParserConstants.IDENTIFIER)
                    {
                        if (testContainsCursor(node))
                        {
                            _prefixType = ContentAssistStrategy.PREFIX_TYPE_DOT_COMPLETION;
                            int proposalStartLength = _relativePos - lastToken.beginColumn;
                            if (proposalStartLength < 0) { // Cursor after firstToken start but before lastToken start?
                              proposalStartLength = 0;
                            }
                            _symbolStartPos = lastToken.beginColumn;
                            _symbolEndPos = lastToken.endColumn;
                            _prefixResolved = true;
                        }
                        // only include this suffix on the path if the cursor is
                        // further to the right.  Thus for x.^y we get a prefix "x"
                        // and for x.y.^z we get "x.y" since this the part we must
                        // resolve the prefix for
                        else
                        {
                            _symbolPrefix += node.getFirstToken().image + lastToken.image;
                        }
                    }
                    else if (lastToken == node.getFirstToken())
                    {
                        if (testCursorImmediatelyAfter(node))
                        {
                            _prefixType = ContentAssistStrategy.PREFIX_TYPE_DOT_COMPLETION;
                            _symbolStartPos = lastToken.endColumn + 1;
                            _symbolEndPos = lastToken.endColumn;
                            _prefixResolved = true;
                        }
                    }
                }

                return null;               
            }
           
            if (node.getFirstToken().kind == JSPELParserConstants.LBRACKET)
            {
                // try to support ca inside the brackets
                node.childrenAccept(this, data);
            }

            Object retValue =  node.childrenAccept(this, data);
               
            if (!_prefixResolved)
            {
                // if we haven't resolved the prefix yet, then we need
                // to append this suffix value
                _symbolPrefix += _fullText.substring(node.getFirstToken().beginColumn-1, node.getLastToken().endColumn);
            }
           
            return retValue;
        }

        public Object visit(SimpleNode node, Object data)
        {
            return node.childrenAccept(this, data);
        }
       
        private boolean testCursorImmediatelyAfter(SimpleNode node)
        {
            return node.getLastToken().endColumn == _relativePos-1;
        }
       
        /**
         * "Containing a cursor" here is deemed to mean that current cursor
         * position as indicated by _relativePos, is either directly before, on or
         * directly after an expression.  For example, in a Value expression like
         *
         *          x x x . y y y . z z z
         *         ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
         *         1 2 3 4 5 6 7 8 9 0 1 2
         *        
         * Position's 1-4 are on xxx, 5-8 are on yyy and 9-12 are on zzz
         *
         * @param node
         * @return true if the node "contains the cursor" (see above)
         */
        private boolean testContainsCursor(SimpleNode node)
        {
            return (node.getFirstToken().beginColumn <= _relativePos
                    && node.getLastToken().endColumn+1 >= _relativePos);
               
        }
    }
   
    private ContentAssistParser()
    {
        // utility class; not instantiable
    }
}
TOP

Related Classes of org.eclipse.jst.jsf.core.internal.contentassist.el.ContentAssistParser$PrefixVisitor

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.