org.eclipselabs.stlipse.javaeditor.JavaCompletionProposalComputer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipselabs.stlipse.javaeditor.JavaCompletionProposalComputer.java

Source

/*-
 * Copyright (C) 2011-2014 by Iwao AVE!
 * This program is made available under the terms of the MIT License.
 */

package org.eclipselabs.stlipse.javaeditor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.CompletionContext;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipselabs.stlipse.Activator;
import org.eclipselabs.stlipse.cache.BeanPropertyCache;
import org.eclipselabs.stlipse.cache.BeanPropertyVisitor;

/**
 * @author Iwao AVE!
 */
public class JavaCompletionProposalComputer implements IJavaCompletionProposalComputer {
    private static String VALIDATE_NESTED = "ValidateNestedProperties";

    private static String VALIDATE = "Validate";

    private static String STRICT_BINDING = "StrictBinding";

    private static String AFTER = "After";

    private static String BEFORE = "Before";

    private static String VALIDATION_METHOD = "ValidationMethod";

    private static String WIZARD = "Wizard";

    public void sessionStarted() {
        // Nothing todo for now.
    }

    public List<ICompletionProposal> computeCompletionProposals(ContentAssistInvocationContext context,
            IProgressMonitor monitor) {
        List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
        if (context instanceof JavaContentAssistInvocationContext) {
            JavaContentAssistInvocationContext javaContext = (JavaContentAssistInvocationContext) context;
            CompletionContext coreContext = javaContext.getCoreContext();
            ICompilationUnit unit = javaContext.getCompilationUnit();
            try {
                if (unit != null && unit.isStructureKnown()) {
                    int offset = javaContext.getInvocationOffset();
                    IJavaElement element = unit.getElementAt(offset);
                    if (element != null && element instanceof IAnnotatable) {
                        ValueInfo valueInfo = scanAnnotation(offset, (IAnnotatable) element);
                        if (valueInfo != null) {
                            int replacementLength = valueInfo.getValueLength();
                            IJavaProject project = javaContext.getProject();
                            String beanFqn = unit.getType(element.getParent().getElementName())
                                    .getFullyQualifiedName();
                            if (valueInfo.isField()) {
                                if (valueInfo.isPropertyOmmitted()) {
                                    StringBuilder matchStr = resolveBeanPropertyName(element);
                                    if (matchStr.length() > 0) {
                                        char[] token = coreContext.getToken();
                                        matchStr.append('.').append(token);
                                        Map<String, String> fields = BeanPropertyCache.searchFields(project,
                                                beanFqn, matchStr.toString(), false, -1, false);
                                        proposals.addAll(BeanPropertyCache.buildFieldNameProposal(fields,
                                                String.valueOf(token), coreContext.getTokenStart() + 1,
                                                replacementLength));
                                    }
                                } else {
                                    String input = String.valueOf(coreContext.getToken());
                                    Map<String, String> fields = BeanPropertyCache.searchFields(project, beanFqn,
                                            input, false, -1, false);
                                    proposals.addAll(BeanPropertyCache.buildFieldNameProposal(fields, input,
                                            coreContext.getTokenStart() + 1, replacementLength));
                                }
                            } else if (valueInfo.isEventHandler()) {
                                String input = String.valueOf(coreContext.getToken());
                                boolean isNot = false;
                                if (input.length() > 0 && input.startsWith("!")) {
                                    isNot = true;
                                    input = input.length() > 1 ? input.substring(1) : "";
                                }
                                List<String> events = BeanPropertyCache.searchEventHandler(project, beanFqn, input,
                                        false, false);
                                int relevance = events.size();
                                for (String event : events) {
                                    String replaceStr = isNot ? "!" + event : event;
                                    ICompletionProposal proposal = new JavaCompletionProposal(replaceStr,
                                            coreContext.getTokenStart() + 1, replacementLength, replaceStr.length(),
                                            Activator.getIcon(), event, null, null, relevance--);
                                    proposals.add(proposal);
                                }
                            }
                        }
                    }
                }
            } catch (JavaModelException e) {
                Activator.log(Status.ERROR, "Something went wrong.", e);
            }
        }
        return proposals;
    }

    private StringBuilder resolveBeanPropertyName(IJavaElement element) throws JavaModelException {
        StringBuilder result = new StringBuilder();
        int elementType = element.getElementType();
        String elementName = element.getElementName();
        if (elementType == IJavaElement.FIELD) {
            result.append(elementName);
        } else if (elementType == IJavaElement.METHOD) {
            IMethod method = (IMethod) element;
            if (Flags.isPublic(method.getFlags()) && (isSetter(method) || isGetter(method))) {
                result.append(BeanPropertyVisitor.getFieldNameFromAccessor(elementName));
            }
        }
        return result;
    }

    private boolean isSetter(IMethod method) throws JavaModelException {
        String name = method.getElementName();
        return isVoid(method) && name.startsWith("set") && name.length() > 3;
    }

    private boolean isGetter(IMethod method) throws JavaModelException {
        String name = method.getElementName();
        return !isVoid(method)
                && ((name.startsWith("get") && name.length() > 3) || name.startsWith("is") && name.length() > 2);
    }

    private boolean isVoid(IMethod method) throws JavaModelException {
        return String.valueOf(Signature.C_VOID).equals(method.getReturnType());
    }

    private ValueInfo scanAnnotation(int offset, IAnnotatable annotatable) throws JavaModelException {
        IAnnotation[] annotations = annotatable.getAnnotations();
        for (IAnnotation annotation : annotations) {
            ISourceRange sourceRange = annotation.getSourceRange();
            if (isInRange(sourceRange, offset)) {
                String annotationName = annotation.getElementName();
                if (VALIDATE_NESTED.equals(annotationName)) {
                    // parse nested @Validate
                    IMemberValuePair[] valuePairs = annotation.getMemberValuePairs();
                    for (IMemberValuePair valuePair : valuePairs) {
                        if ("value".equals(valuePair.getMemberName())
                                && valuePair.getValueKind() == IMemberValuePair.K_ANNOTATION) {
                            // the value is an array of IAnnotation
                            Object[] validates = (Object[]) valuePair.getValue();
                            for (Object validate : validates) {
                                IAnnotation validateAnnotation = (IAnnotation) validate;
                                ISourceRange validateSourceRange = validateAnnotation.getSourceRange();
                                if (isInRange(validateSourceRange, offset))
                                    return parseAnnotation(offset, validateAnnotation, validateSourceRange);
                            }
                        }
                    }
                } else if (isNonNestedSupportedAnnotation(annotationName)) {
                    return parseAnnotation(offset, annotation, sourceRange);
                }
            }
        }
        return null;
    }

    private static boolean isNonNestedSupportedAnnotation(String annotationName) {
        final List<String> annotations = Arrays.asList(VALIDATE, STRICT_BINDING, BEFORE, AFTER, VALIDATION_METHOD,
                WIZARD);
        return annotations.contains(annotationName);
    }

    private ValueInfo parseAnnotation(int offset, IAnnotation annotation, ISourceRange sourceRange)
            throws JavaModelException {
        int annotationOffset = sourceRange.getOffset();
        String source = annotation.getSource();
        int index = source.indexOf('(', annotation.getElementName().length() + 1);
        if (index > -1) {
            int stringValueLength = 0;
            boolean scanningName = true;
            boolean scanningValue = false;
            boolean inStringValue = false;
            boolean inArrayValue = false;
            boolean escaped = false;
            StringBuilder optionName = new StringBuilder();
            for (index++; index < source.length(); index++) {
                char c = source.charAt(index);
                if (scanningName) {
                    if (c == '=')
                        scanningName = false;
                    else if (c != ' ')
                        optionName.append(c);
                } else if (!scanningValue) {
                    if (c != ' ') {
                        scanningValue = true;
                        if (c == '"') {
                            inStringValue = true;
                            stringValueLength = 0;
                        } else if (c == '{')
                            inArrayValue = true;
                    }
                } else {
                    // scanning value part
                    if (inStringValue) {
                        if (escaped)
                            ; // ignore the escaped character
                        else if (c == '\\')
                            escaped = true;
                        else if (c == '"') {
                            if (annotationOffset + index >= offset)
                                break;
                            inStringValue = false;
                            stringValueLength = 0;
                        }
                        if (inStringValue)
                            stringValueLength++;
                    } else if (inArrayValue) {
                        if (c == '"')
                            inStringValue = true;
                        else if (c == '}')
                            inArrayValue = false;
                        else
                            ; // ignore
                    } else if (c == ',') {
                        scanningValue = false;
                        scanningName = true;
                        optionName.setLength(0);
                    }
                }
            }
            if (optionName.length() > 0) {
                return new ValueInfo(annotation.getElementName(), optionName.toString(), stringValueLength);
            }
        }
        return null;
    }

    private boolean isInRange(ISourceRange sourceRange, int offset) {
        int start = sourceRange.getOffset();
        int end = start + sourceRange.getLength();
        return start <= offset && offset <= end;
    }

    public List<IContextInformation> computeContextInformation(ContentAssistInvocationContext context,
            IProgressMonitor monitor) {
        return Collections.emptyList();
    }

    public String getErrorMessage() {
        return null;
    }

    public void sessionEnded() {
        // Nothing todo for now.
    }

    static class ValueInfo {
        private String annotationName;

        private String attributeName;

        private int valueLength;

        public ValueInfo(String annotationName, String attributeName, int valueLength) {
            super();
            this.annotationName = annotationName;
            this.attributeName = attributeName;
            this.valueLength = valueLength;
        }

        public boolean isField() {
            return (STRICT_BINDING.equals(annotationName)
                    && ("allow".equals(attributeName) || "deny".equals(attributeName)))
                    || (VALIDATE.equals(annotationName) && "field".equals(attributeName));
        }

        public boolean isPropertyOmmitted() {
            return !STRICT_BINDING.equals(annotationName);
        }

        public boolean isEventHandler() {
            return (("on".equals(attributeName) && (VALIDATE.equals(annotationName) || AFTER.equals(annotationName)
                    || BEFORE.equals(annotationName) || VALIDATION_METHOD.equals(annotationName))))
                    || ("startEvents".equals(attributeName) && WIZARD.equals(annotationName));
        }

        public String getAnnotationName() {
            return annotationName;
        }

        public String getAttributeName() {
            return attributeName;
        }

        public int getValueLength() {
            return valueLength;
        }
    }
}