org.jboss.tools.common.el.ui.ca.ELProposalProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.tools.common.el.ui.ca.ELProposalProcessor.java

Source

/*******************************************************************************
 * Copyright (c) 2007-2013 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is 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
 *
 * Contributor:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/

package org.jboss.tools.common.el.ui.ca;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jface.internal.text.html.BrowserInformationControl;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4;
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.StructuredModelManager;
import org.eclipse.wst.sse.ui.internal.contentassist.IRelevanceCompletionProposal;
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.ui.internal.contentassist.AbstractContentAssistProcessor;
import org.eclipse.wst.xml.ui.internal.util.SharedXMLEditorPluginImageHelper;
import org.jboss.tools.common.el.core.ELReference;
import org.jboss.tools.common.el.core.ca.AbstractELCompletionEngine;
import org.jboss.tools.common.el.core.ca.ELTextProposal;
import org.jboss.tools.common.el.core.ca.MessagesELTextProposal;
import org.jboss.tools.common.el.core.model.ELInvocationExpression;
import org.jboss.tools.common.el.core.resolver.ELContext;
import org.jboss.tools.common.el.core.resolver.ELResolver;
import org.jboss.tools.common.el.core.resolver.ELResolverFactoryManager;
import org.jboss.tools.common.el.ui.ElUiPlugin;
import org.jboss.tools.common.el.ui.internal.info.ELInfoHover;
import org.jboss.tools.common.el.ui.internal.info.Messages;
import org.jboss.tools.common.text.TextProposal;
import org.jboss.tools.common.text.ext.util.Utils;
import org.jboss.tools.common.ui.CommonUIPlugin;
import org.jboss.tools.common.util.EclipseUIUtil;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

/**
 * Content assist proposal processor.
 * Computes Seam EL proposals.
 * 
 * @author Victor Rubezhny
 */
@SuppressWarnings("restriction")
public abstract class ELProposalProcessor extends AbstractContentAssistProcessor {

    private static final ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0];
    private static final IContextInformation[] NO_CONTEXTS = new IContextInformation[0];

    /*
     * Sorts IJavaElements by IJavaElement.getElementName() case insensitive order
     */
    public static final Comparator<IJavaElement> CASE_INSENSITIVE_ORDER = new Comparator<IJavaElement>() {
        public int compare(IJavaElement o1, IJavaElement o2) {
            return String.CASE_INSENSITIVE_ORDER.compare(o1.getElementName(), o2.getElementName());
        }
    };

    public static final class Proposal implements ICompletionProposal, ICompletionProposalExtension,
            ICompletionProposalExtension2, ICompletionProposalExtension3, ICompletionProposalExtension4,
            ICompletionProposalExtension5, IRelevanceCompletionProposal {

        private final String fString;
        private final String fPrefix;
        private final String fNewPrefix;
        private final int fOffset;
        private int fNewPosition;
        private String fDisplayString;
        private String fAlternateMatch;
        private Object fAdditionalProposalInfo;
        private IJavaElement[] fJavaElements;
        private MessagesELTextProposal fPropertySource;
        /**
         * The control creator.
         */
        private IInformationControlCreator fCreator;

        private Image fImage;

        public Proposal(String string, String prefix, int offset) {
            this(string, prefix, offset, offset + string.length());
        }

        public Proposal(String string, String prefix, int offset, int newPosition) {
            this(string, prefix, prefix, offset, offset + string.length(), null, null, null, null, null, null);
        }

        /**/ public Proposal(String string, String prefix, int offset, int newPosition, Image image,
                String displayString, String alternateMatch, String additionalProposalInfo,
                IJavaElement[] javaElements, MessagesELTextProposal propertySource) {
            this(string, prefix, prefix, offset, offset + string.length(), image, displayString, alternateMatch,
                    additionalProposalInfo, javaElements, propertySource);
        }

        public Proposal(String string, String prefix, String newPrefix, int offset, int newPosition) {
            this(string, prefix, newPrefix, offset, newPosition, null, null, null, null, null, null);
        }

        /**/ public Proposal(String string, String prefix, String newPrefix, int offset, int newPosition,
                Image image, String displayString, String alternateMatch, String additionalProposalInfo,
                IJavaElement[] javaElements, MessagesELTextProposal propertySource) {
            fString = string;
            fPrefix = prefix;
            fNewPrefix = newPrefix;
            fOffset = offset;
            fNewPosition = newPosition;
            fImage = image;
            fDisplayString = displayString;
            fAlternateMatch = alternateMatch;
            fAdditionalProposalInfo = additionalProposalInfo;
            fJavaElements = javaElements;
            if (fJavaElements != null && fJavaElements.length > 0)
                fAdditionalProposalInfo = null; // Drop it due to valculate it later based on java elements
            fPropertySource = propertySource;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(IDocument)
         */
        public void apply(IDocument document) {
            apply(null, '\0', 0, fOffset);
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(IDocument)
         */
        public Point getSelection(IDocument document) {
            return new Point(getCursorPosition(), 0);
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
         */
        public String getAdditionalProposalInfo() {
            Object info = getAdditionalProposalInfo(new NullProgressMonitor());
            return info == null ? null : info.toString();
        }

        private static final String EMPTY_ADDITIONAL_INFO = new String();

        @Override
        public Object getAdditionalProposalInfo(IProgressMonitor monitor) {
            if (fAdditionalProposalInfo == null) {
                if (this.fJavaElements != null && this.fJavaElements.length > 0) {
                    fAdditionalProposalInfo = ELInfoHover.getHoverInfo(fJavaElements, monitor);
                } else if (fPropertySource != null) {
                    fAdditionalProposalInfo = ELInfoHover.getHoverInfo(fPropertySource.getBaseName(),
                            fPropertySource.getPropertyName(), fPropertySource.getAllObjects(), monitor);
                }
            }
            if (fAdditionalProposalInfo == null)
                fAdditionalProposalInfo = EMPTY_ADDITIONAL_INFO;
            return fAdditionalProposalInfo;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
         */
        public String getDisplayString() {
            if (fDisplayString != null)
                return fDisplayString;

            String dispString = (fNewPrefix == null ? fPrefix : fNewPrefix) + fString;
            if (dispString != null) {
                if (dispString.indexOf('{') == -1 && dispString.indexOf('}') != -1) {
                    dispString = dispString.substring(0, dispString.indexOf('}'));
                }
                if (dispString.indexOf('{') != -1 && dispString.indexOf('}') == -1) {
                    dispString += "}"; //$NON-NLS-1$
                }
            }
            return dispString;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
         */
        public Image getImage() {
            return fImage == null
                    ? SharedXMLEditorPluginImageHelper.getImage(SharedXMLEditorPluginImageHelper.IMG_OBJ_ATTRIBUTE)
                    : fImage;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
         */
        public IContextInformation getContextInformation() {
            return null;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#apply(IDocument, char, int)
         */
        public void apply(IDocument document, char trigger, int offset) {
            try {
                int docCharsToReplace = (fNewPrefix == null || fPrefix == null) ? 0
                        : fPrefix.length() - fNewPrefix.length();
                String replacement = fString.substring(offset - fOffset);
                document.replace(offset - docCharsToReplace, docCharsToReplace, replacement);
            } catch (BadLocationException x) {
                ElUiPlugin.getDefault().logError(x);
            }
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#isValidFor(IDocument, int)
         */
        public boolean isValidFor(IDocument document, int offset) {
            return validate(document, offset, null);
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#getTriggerCharacters()
         */
        public char[] getTriggerCharacters() {
            return null;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension#getContextInformationPosition()
         */
        public int getContextInformationPosition() {
            return 0;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply(ITextViewer, char, int, int)
         */
        public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) {
            apply(viewer.getDocument(), trigger, offset);

            if (fString != null && fString.endsWith("}")) { //$NON-NLS-1$
                fNewPosition -= 1;
            }
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(ITextViewer, boolean)
         */
        public void selected(ITextViewer viewer, boolean smartToggle) {
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(ITextViewer)
         */
        public void unselected(ITextViewer viewer) {
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(IDocument document, int offset, DocumentEvent event)
         */
        public boolean validate(IDocument document, int offset, DocumentEvent event) {
            try {
                int prefixStart = fOffset - fPrefix.length();
                return offset >= fOffset && ((offset < fOffset + fString.length()
                        && document.get(prefixStart, offset - (prefixStart))
                                .equals((fPrefix + fString).substring(0, offset - prefixStart)))
                        || (fAlternateMatch != null && offset < fOffset + fAlternateMatch.length()
                                && document.get(prefixStart, offset - (prefixStart))
                                        .equals((fPrefix + fAlternateMatch).substring(0, offset - prefixStart))));
            } catch (BadLocationException x) {
                return false;
            }
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
         */
        public IInformationControlCreator getInformationControlCreator() {
            final boolean[] browserInformationControlAvailable = new boolean[] { false };
            Display.getDefault().syncExec(new Runnable() {
                @Override
                public void run() {
                    Shell shell = JavaPlugin.getActiveWorkbenchShell();
                    browserInformationControlAvailable[0] = (shell != null
                            && BrowserInformationControl.isAvailable(shell));
                }
            });

            if (!browserInformationControlAvailable[0])
                return null;

            if (fCreator == null) {
                ELInfoHover.PresenterControlCreator presenterControlCreator = new ELInfoHover.PresenterControlCreator(
                        ELInfoHover.getSite());
                fCreator = new ELInfoHover.HoverControlCreator(presenterControlCreator,
                        Messages.additionalInfo_affordance);
            }
            return fCreator;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getPrefixCompletionText(IDocument, int)
         */
        public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
            return fPrefix + fString;
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getPrefixCompletionStart(IDocument, int)
         */
        public int getPrefixCompletionStart(IDocument document, int completionOffset) {
            return fOffset - fPrefix.length();
        }

        /*
         * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension4#isAutoInsertable()
         */
        public boolean isAutoInsertable() {
            return false;
        }

        /**
         * Return cursor position of proposal replacement string.
         */
        public int getCursorPosition() {
            int cursorPosition = -1;

            int openingQuoteInReplacement = fString.lastIndexOf('(');
            int closingQuoteInReplacement = fString.lastIndexOf(')');
            int openingQuoteInDisplay = fDisplayString.lastIndexOf('(');
            int closingQuoteInDisplay = fDisplayString.lastIndexOf(')');

            if (openingQuoteInReplacement != -1 && closingQuoteInReplacement != -1 && openingQuoteInDisplay != -1
                    && closingQuoteInDisplay != -1 && (closingQuoteInReplacement
                            - openingQuoteInReplacement) != (closingQuoteInDisplay - openingQuoteInDisplay)) {
                cursorPosition = openingQuoteInReplacement + 1;
            }

            return cursorPosition > -1 ? fOffset + cursorPosition : fNewPosition;
        }

        /**
         * Returns the relevance of the proposal
         */
        public int getRelevance() {
            return TextProposal.R_JSP_JSF_EL_VARIABLE_ATTRIBUTE_VALUE + 10;
        }

        public String getfAlternateMatch() {
            return fAlternateMatch;
        }

        public void setfAlternateMatch(String fAlternateMatch) {
            this.fAlternateMatch = fAlternateMatch;
        }
    }

    /**
     * Creates a new Seam EL completion proposal computer.
     */
    public ELProposalProcessor() {
    }

    /*
     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
     */
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
        IDocument document = viewer.getDocument();
        int start = 0;
        int end = document.getLength();

        int[] region = getRegion(document, offset);
        if (region != null) {
            start = region[0];
            end = region[1];
        }

        return computeCompletionProposals(viewer, offset, start, end);
    }

    private int[] getRegion(IDocument document, int offset) {
        IStructuredModel model = StructuredModelManager.getModelManager().getExistingModelForRead(document);

        try {
            Document xmlDocument = (model instanceof IDOMModel) ? ((IDOMModel) model).getDocument() : null;
            if (xmlDocument == null)
                return null;

            Node n = Utils.findNodeForOffset(xmlDocument, offset);

            if (n == null || !(n instanceof Attr || n instanceof Text))
                return null;

            int start = 0;
            int end = document.getLength();
            if (n instanceof IDOMNode) {
                start = ((IDOMNode) n).getStartOffset();
                end = ((IDOMNode) n).getEndOffset();
            }
            return new int[] { start, end };
        } finally {
            if (model != null) {
                model.releaseFromRead();
                model = null;
            }
        }
    }

    abstract protected boolean isEnabled(IFile file);

    abstract protected Image getImage();

    abstract protected ELContext getELContext(IFile file);

    /**
     * 
     * @param start  start of relevant region in document
     * @param end    end of relevant region in document
     * 
     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
     */
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset, int start, int end) {
        ITextEditor part = EclipseUIUtil.getActiveEditor();
        if (part == null) {
            return NO_PROPOSALS;
        }

        IEditorInput editorInput = part.getEditorInput();
        if (!(editorInput instanceof IFileEditorInput)) {
            return NO_PROPOSALS;
        }

        IFile file = ((IFileEditorInput) editorInput).getFile();
        if (!isEnabled(file)) {
            return NO_PROPOSALS;
        }

        ELContext context = getELContext(file);
        if (context == null) {
            return NO_PROPOSALS;
        }

        ELReference ref = context.getELReference(offset);
        if (ref == null) {
            return NO_PROPOSALS;
        }

        ELResolver[] resolvers = ELResolverFactoryManager.getInstance().getResolvers(file);

        List<ICompletionProposal> resultList = new ArrayList<ICompletionProposal>();

        String prefix = getPrefix(viewer, offset, start, end);

        prefix = (prefix == null ? "" : prefix); //$NON-NLS-1$

        String proposalPrefix = ""; //$NON-NLS-1$
        String proposalSufix = ""; //$NON-NLS-1$
        String elStartChar = "#"; //$NON-NLS-1$
        String documentContent = ref.getELModel().getSource();
        // Is '#{', or '${', or '}'-bracket exists? If not - add the 
        int elEndPosition = documentContent.indexOf("#{", offset - ref.getStartPosition()); //$NON-NLS-1$
        int limit = documentContent.indexOf("${", offset - ref.getStartPosition()); //$NON-NLS-1$
        if (limit != -1 && elEndPosition != -1 && limit < elEndPosition) {
            elEndPosition = limit;
        }
        limit = documentContent.indexOf('}', offset - ref.getStartPosition());
        if (limit != -1 && (elEndPosition == -1 || (elEndPosition != -1 && limit < elEndPosition))) {
            elEndPosition = limit + 1;
        }
        String restOfEL = elEndPosition == -1 ? "" //$NON-NLS-1$
                : documentContent.substring(offset - ref.getStartPosition(), elEndPosition);
        if (restOfEL.indexOf('}') == -1) {
            proposalSufix = "}"; //$NON-NLS-1$
        }

        for (int i = 0; i < resolvers.length; i++) {
            List<TextProposal> suggestions = resolvers[i].getProposals(context, offset);
            List<TextProposal> uniqueSuggestions = AbstractELCompletionEngine.makeProposalsUnique(suggestions);

            for (TextProposal kbProposal : uniqueSuggestions) {
                String string = kbProposal.getReplacementString();
                Image image = kbProposal.hasImage()
                        ? CommonUIPlugin.getImageDescriptorRegistry().get(kbProposal.getImageDescriptor())
                        : getImage();
                if (string.length() >= 0) {
                    string = proposalPrefix + string;
                    if (string.length() > 0 && ('#' == string.charAt(0) || '$' == string.charAt(0)))
                        string = elStartChar + string.substring(1);

                    String additionalProposalInfo = kbProposal.getContextInfo();
                    IJavaElement[] javaElements = null;
                    MessagesELTextProposal source = null;

                    if (kbProposal instanceof ELTextProposal) {
                        javaElements = ((ELTextProposal) kbProposal).getAllJavaElements();
                    } else if (kbProposal instanceof MessagesELTextProposal) {
                        source = (MessagesELTextProposal) kbProposal;
                    }

                    if (string.startsWith("[") && prefix != null && prefix.indexOf('.') != -1) { //$NON-NLS-1$
                        String newPrefix = prefix.substring(0, prefix.lastIndexOf('.'));
                        if (string.indexOf('\'') != -1 && restOfEL.indexOf('\'') != -1) // Exclude last quote if this char already exists
                            string = string.substring(0, string.lastIndexOf('\''));

                        if (string.indexOf(']') == -1 && restOfEL.indexOf(']') == -1) // Add closing square bracket if needed
                            string += ']';

                        string += proposalSufix;
                        resultList.add(new Proposal(string, prefix, newPrefix, offset,
                                offset - (prefix.length() - newPrefix.length()) + string.length()
                                        - proposalSufix.length(),
                                image, kbProposal.getLabel(), kbProposal.getAlternateMatch(),
                                additionalProposalInfo, javaElements, source));
                    } else {
                        if (string.indexOf('\'') != -1 && restOfEL.indexOf('\'') != -1) // Exclude last quote if this char already exists
                            string = string.substring(0, string.lastIndexOf('\''));

                        if ((string.indexOf('[') != -1 || (prefix.indexOf('[') != -1
                                && prefix.indexOf(']', prefix.lastIndexOf('[')) == -1)) && string.indexOf(']') == -1
                                && restOfEL.indexOf(']') == -1) // Add closing square bracket if needed
                            string += ']';

                        string += proposalSufix;
                        resultList.add(new Proposal(string, prefix, offset,
                                offset + string.length() - proposalSufix.length(), image, kbProposal.getLabel(),
                                kbProposal.getAlternateMatch(), additionalProposalInfo, javaElements, source));
                    }
                }
            }
        }
        if (resultList.isEmpty()) {
            return NO_PROPOSALS;
        }

        ICompletionProposal[] resultArray = new ICompletionProposal[resultList.size()];
        resultArray = resultList.toArray(resultArray);
        Arrays.sort(resultArray, new Comparator<ICompletionProposal>() {
            public int compare(ICompletionProposal arg0, ICompletionProposal arg1) {
                return arg0.getDisplayString().compareTo(arg1.getDisplayString());
            }
        });
        return resultArray;
    }

    /*
     * @return non-paired quote char if exists, otherwise 0
     */
    private char getPreceedingQuoteChar(int initialOffset, String restOfCurrentValue) {
        int offset = initialOffset;

        char inQuotesChar = 0;
        while (--offset >= 0) {
            if ('"' == restOfCurrentValue.charAt(offset) || '\'' == restOfCurrentValue.charAt(offset)
                    && (inQuotesChar == 0 || inQuotesChar == restOfCurrentValue.charAt(offset))) {
                if (initialOffset + offset > 0 && restOfCurrentValue.charAt(offset - 1) == '\\') {
                    int backslashCount = 1;
                    while ((offset - backslashCount) >= 0
                            && restOfCurrentValue.charAt(offset - backslashCount) == '\\') {
                        backslashCount++;
                    }

                    if (backslashCount % 2 == 1) {
                        inQuotesChar = inQuotesChar == 0 ? restOfCurrentValue.charAt(offset) : 0;
                        offset -= backslashCount;
                    }
                } else {
                    inQuotesChar = inQuotesChar == 0 ? restOfCurrentValue.charAt(offset) : 0;
                }
            } else if ('{' == restOfCurrentValue.charAt(offset)) {
                if (offset > 0 && ('#' == restOfCurrentValue.charAt(offset - 1)
                        || '$' == restOfCurrentValue.charAt(offset - 1))) {
                    return inQuotesChar;
                }
            }
        }
        return inQuotesChar;
    }

    /*
     * Checks if the EL operand ending character is present
     * @return
     */
    private int getELEndPosition(int initialOffset, String restOfCurrentValue) {
        int offset = -1;

        char inQuotesChar = getPreceedingQuoteChar(initialOffset, restOfCurrentValue);
        while (++offset < restOfCurrentValue.length() - initialOffset) {
            if (inQuotesChar == 0) {
                if ('}' == restOfCurrentValue.charAt(initialOffset + offset))
                    return offset;

                if ('#' == restOfCurrentValue.charAt(initialOffset + offset))
                    return -1;

                if ('<' == restOfCurrentValue.charAt(initialOffset + offset))
                    return -1;

                if ('>' == restOfCurrentValue.charAt(initialOffset + offset))
                    return -1;

                if ('/' == restOfCurrentValue.charAt(initialOffset + offset)
                        && (initialOffset + offset + 1 < restOfCurrentValue.length()
                                && '>' == restOfCurrentValue.charAt(initialOffset + offset + 1)))
                    return -1;

                if ('"' == restOfCurrentValue.charAt(initialOffset + offset)
                        || '\'' == restOfCurrentValue.charAt(initialOffset + offset)) {
                    inQuotesChar = restOfCurrentValue.charAt(initialOffset + offset);
                }

                if ('\\' == restOfCurrentValue.charAt(initialOffset + offset)) {
                    int backslashCount = 1;

                    while ((initialOffset + offset + backslashCount) < restOfCurrentValue.length()
                            && restOfCurrentValue.charAt(initialOffset + offset + backslashCount) == '\\') {
                        backslashCount++;
                    }

                    if (initialOffset + offset + backslashCount >= restOfCurrentValue.length())
                        return -1;

                    if (backslashCount % 2 == 1 && ('"' == restOfCurrentValue
                            .charAt(initialOffset + offset + backslashCount)
                            || '\'' == restOfCurrentValue.charAt(initialOffset + offset + backslashCount))) {
                        inQuotesChar = restOfCurrentValue.charAt(initialOffset + offset + backslashCount);
                        offset += backslashCount;
                    }
                }
            } else {
                if ('"' == restOfCurrentValue.charAt(initialOffset + offset)
                        || '\'' == restOfCurrentValue.charAt(initialOffset + offset)) {
                    inQuotesChar = 0;
                }

                if ('\\' == restOfCurrentValue.charAt(initialOffset + offset)) {
                    int backslashCount = 1;

                    while ((initialOffset + offset + backslashCount) < restOfCurrentValue.length()
                            && restOfCurrentValue.charAt(initialOffset + offset + backslashCount) == '\\') {
                        backslashCount++;
                    }

                    if (initialOffset + offset + backslashCount >= restOfCurrentValue.length())
                        return -1;

                    if (backslashCount % 2 == 1 && ('"' == restOfCurrentValue
                            .charAt(initialOffset + offset + backslashCount)
                            || '\'' == restOfCurrentValue.charAt(initialOffset + offset + backslashCount))) {
                        inQuotesChar = 0;
                        offset += backslashCount;
                    }
                }
            }
        }
        return -1;
    }

    /**
     * Calculates the EX expression operand string
     * 
     * @param viewer
     * @param offset
     * @param start  start of relevant region in document
     * @param end    end of relevant region in document
     * @return
     * @throws BadLocationException
     */
    public String getPrefix(ITextViewer viewer, int offset, int start, int end)
            throws StringIndexOutOfBoundsException {
        IDocument doc = viewer.getDocument();
        if (doc == null || offset > doc.getLength())
            return null;
        return getPrefix(doc, offset, start, end);
    }

    /**
     * Calculates the EX expression operand string
     * 
     * @param viewer
     * @param offset
     * @param start  start of relevant region in document
     * @param end    end of relevant region in document
     * @return
     * @throws StringIndexOutOfBoundsException
     */
    public String getPrefix(IDocument document, int offset, int start, int end)
            throws StringIndexOutOfBoundsException {
        if (document == null || document.get() == null || offset > document.get().length())
            return null;
        ELInvocationExpression expr = AbstractELCompletionEngine.findExpressionAtOffset(document, offset, start,
                end);
        if (expr == null)
            return null;
        return document.get().substring(expr.getStartPosition(), offset);
    }

    /*
     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
     */
    public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
        // no context informations for Seam EL completions
        return NO_CONTEXTS;
    }

    private char[] autoActivChars;

    /*
     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
     */
    public char[] getCompletionProposalAutoActivationCharacters() {
        if (autoActivChars == null) {
            IPreferenceStore store = EditorsUI.getPreferenceStore();
            String superDefaultChars = store
                    .getDefaultString(PreferenceConstants.CODEASSIST_AUTOACTIVATION_TRIGGERS_JAVA);
            StringBuffer redhatDefaultChars = new StringBuffer(superDefaultChars);
            if (superDefaultChars.indexOf("{") < 0) { //$NON-NLS-1$
                redhatDefaultChars.append('{');
            }
            if (superDefaultChars.indexOf(".") < 0) { //$NON-NLS-1$
                redhatDefaultChars.append('.');
            }
            if (superDefaultChars.indexOf("[") < 0) { //$NON-NLS-1$
                redhatDefaultChars.append('[');
            }

            autoActivChars = new char[redhatDefaultChars.length()];
            redhatDefaultChars.getChars(0, redhatDefaultChars.length(), autoActivChars, 0);
            store.setDefault(PreferenceConstants.CODEASSIST_AUTOACTIVATION_TRIGGERS_JAVA,
                    redhatDefaultChars.toString());
            store.setValue(PreferenceConstants.CODEASSIST_AUTOACTIVATION_TRIGGERS_JAVA,
                    redhatDefaultChars.toString());
        }
        return autoActivChars;
    }

    /*
     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
     */
    public char[] getContextInformationAutoActivationCharacters() {
        return null;
    }

    /*
     * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
     */
    public IContextInformationValidator getContextInformationValidator() {
        return null;
    }

    /*
     * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#getErrorMessage()
     */
    public String getErrorMessage() {
        return null; // no custom error message
    }
}