org.eclipse.che.jdt.internal.core.search.matching.ConstructorLocator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.che.jdt.internal.core.search.matching.ConstructorLocator.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2012 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.che.jdt.internal.core.search.matching;

import org.eclipse.che.jdt.core.search.SearchMatch;
import org.eclipse.che.jdt.core.search.SearchPattern;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedGenericMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;

public class ConstructorLocator extends PatternLocator {

    protected ConstructorPattern pattern;

    public ConstructorLocator(ConstructorPattern pattern) {
        super(pattern);

        this.pattern = pattern;
    }

    protected int fineGrain() {
        return this.pattern.fineGrain;
    }

    public int match(ASTNode node, MatchingNodeSet nodeSet) { // interested in ExplicitConstructorCall
        if (!this.pattern.findReferences)
            return IMPOSSIBLE_MATCH;
        if (!(node instanceof ExplicitConstructorCall))
            return IMPOSSIBLE_MATCH;

        if (!matchParametersCount(node, ((ExplicitConstructorCall) node).arguments))
            return IMPOSSIBLE_MATCH;

        return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
    }

    public int match(ConstructorDeclaration node, MatchingNodeSet nodeSet) {
        int referencesLevel = this.pattern.findReferences ? matchLevelForReferences(node) : IMPOSSIBLE_MATCH;
        int declarationsLevel = this.pattern.findDeclarations ? matchLevelForDeclarations(node) : IMPOSSIBLE_MATCH;

        return nodeSet.addMatch(node, referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel); // use the stronger
        // match
    }

    public int match(Expression node, MatchingNodeSet nodeSet) { // interested in AllocationExpression
        if (!this.pattern.findReferences)
            return IMPOSSIBLE_MATCH;
        if (!(node instanceof AllocationExpression))
            return IMPOSSIBLE_MATCH;

        // constructor name is simple type name
        AllocationExpression allocation = (AllocationExpression) node;
        char[][] typeName = allocation.type.getTypeName();
        if (this.pattern.declaringSimpleName != null
                && !matchesName(this.pattern.declaringSimpleName, typeName[typeName.length - 1]))
            return IMPOSSIBLE_MATCH;

        if (!matchParametersCount(node, allocation.arguments))
            return IMPOSSIBLE_MATCH;

        return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
    }

    public int match(FieldDeclaration field, MatchingNodeSet nodeSet) {
        if (!this.pattern.findReferences)
            return IMPOSSIBLE_MATCH;
        // look only for enum constant
        if (field.type != null || !(field.initialization instanceof AllocationExpression))
            return IMPOSSIBLE_MATCH;

        AllocationExpression allocation = (AllocationExpression) field.initialization;
        if (field.binding != null && field.binding.declaringClass != null) {
            if (this.pattern.declaringSimpleName != null
                    && !matchesName(this.pattern.declaringSimpleName, field.binding.declaringClass.sourceName()))
                return IMPOSSIBLE_MATCH;
        }

        if (!matchParametersCount(field, allocation.arguments))
            return IMPOSSIBLE_MATCH;

        return nodeSet.addMatch(field, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
    }

    //public int match(MethodDeclaration node, MatchingNodeSet nodeSet) - SKIP IT
    /**
     * Special case for message send in javadoc comment. They can be in fact bound to a constructor.
     * @see "http://bugs.eclipse.org/bugs/show_bug.cgi?id=83285"
     */
    public int match(MessageSend msgSend, MatchingNodeSet nodeSet) {
        if ((msgSend.bits & ASTNode.InsideJavadoc) == 0)
            return IMPOSSIBLE_MATCH;
        if (!this.pattern.findReferences)
            return IMPOSSIBLE_MATCH;
        if (this.pattern.declaringSimpleName == null
                || CharOperation.equals(msgSend.selector, this.pattern.declaringSimpleName)) {
            return nodeSet.addMatch(msgSend, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
        }
        return IMPOSSIBLE_MATCH;
    }

    public int match(ReferenceExpression node, MatchingNodeSet nodeSet) {
        if (!this.pattern.findReferences || node.isMethodReference())
            return IMPOSSIBLE_MATCH;
        return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
    }

    //public int match(Reference node, MatchingNodeSet nodeSet) - SKIP IT
    public int match(TypeDeclaration node, MatchingNodeSet nodeSet) {
        if (!this.pattern.findReferences)
            return IMPOSSIBLE_MATCH;

        // need to look for a generated default constructor
        return nodeSet.addMatch(node, this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH);
    }
    //public int match(TypeReference node, MatchingNodeSet nodeSet) - SKIP IT

    protected int matchConstructor(MethodBinding constructor) {
        if (!constructor.isConstructor())
            return IMPOSSIBLE_MATCH;

        // declaring type, simple name has already been matched by matchIndexEntry()
        int level = resolveLevelForType(this.pattern.declaringSimpleName, this.pattern.declaringQualification,
                constructor.declaringClass);
        if (level == IMPOSSIBLE_MATCH)
            return IMPOSSIBLE_MATCH;

        // parameter types
        int parameterCount = this.pattern.parameterCount;
        if (parameterCount > -1) {
            if (constructor.parameters == null)
                return INACCURATE_MATCH;
            if (parameterCount != constructor.parameters.length)
                return IMPOSSIBLE_MATCH;
            for (int i = 0; i < parameterCount; i++) {
                // TODO (frederic) use this call to refine accuracy on parameter types
                //         int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i], this.pattern.parameterQualifications[i], this.pattern.parametersTypeArguments[i], 0, constructor.parameters[i]);
                int newLevel = resolveLevelForType(this.pattern.parameterSimpleNames[i],
                        this.pattern.parameterQualifications[i], constructor.parameters[i]);
                if (level > newLevel) {
                    if (newLevel == IMPOSSIBLE_MATCH) {
                        //               if (isErasureMatch) {
                        //                  return ERASURE_MATCH;
                        //               }
                        return IMPOSSIBLE_MATCH;
                    }
                    level = newLevel; // can only be downgraded
                }
            }
        }
        return level;
    }

    protected int matchContainer() {
        if (this.pattern.findReferences)
            return ALL_CONTAINER; // handles both declarations + references & just references
        // COMPILATION_UNIT_CONTAINER - implicit constructor call: case of Y extends X and Y doesn't define any constructor
        // CLASS_CONTAINER - implicit constructor call: case of constructor declaration with no explicit super call
        // METHOD_CONTAINER - reference in another constructor
        // FIELD_CONTAINER - anonymous in a field initializer

        // declarations are only found in Class
        return CLASS_CONTAINER;
    }

    protected int matchLevelForReferences(ConstructorDeclaration constructor) {
        ExplicitConstructorCall constructorCall = constructor.constructorCall;
        if (constructorCall == null || constructorCall.accessMode != ExplicitConstructorCall.ImplicitSuper)
            return IMPOSSIBLE_MATCH;

        if (this.pattern.parameterSimpleNames != null) {
            int length = this.pattern.parameterSimpleNames.length;
            Expression[] args = constructorCall.arguments;
            int argsLength = args == null ? 0 : args.length;
            if (length != argsLength)
                return IMPOSSIBLE_MATCH;
        }
        return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
    }

    protected int matchLevelForDeclarations(ConstructorDeclaration constructor) {
        // constructor name is stored in selector field
        if (this.pattern.declaringSimpleName != null
                && !matchesName(this.pattern.declaringSimpleName, constructor.selector))
            return IMPOSSIBLE_MATCH;

        if (this.pattern.parameterSimpleNames != null) {
            int length = this.pattern.parameterSimpleNames.length;
            Argument[] args = constructor.arguments;
            int argsLength = args == null ? 0 : args.length;
            if (length != argsLength)
                return IMPOSSIBLE_MATCH;
        }

        // Verify type arguments (do not reject if pattern has no argument as it can be an erasure match)
        if (this.pattern.hasConstructorArguments()) {
            if (constructor.typeParameters == null
                    || constructor.typeParameters.length != this.pattern.constructorArguments.length)
                return IMPOSSIBLE_MATCH;
        }

        return this.pattern.mustResolve ? POSSIBLE_MATCH : ACCURATE_MATCH;
    }

    boolean matchParametersCount(ASTNode node, Expression[] args) {
        if (this.pattern.parameterSimpleNames != null
                && (!this.pattern.varargs || ((node.bits & ASTNode.InsideJavadoc) != 0))) {
            int length = this.pattern.parameterCount;
            if (length < 0)
                length = this.pattern.parameterSimpleNames.length;
            int argsLength = args == null ? 0 : args.length;
            if (length != argsLength) {
                return false;
            }
        }
        return true;
    }

    protected void matchReportReference(ASTNode reference, IJavaElement element, Binding elementBinding,
            int accuracy, MatchLocator locator) throws CoreException {

        MethodBinding constructorBinding = null;
        boolean isSynthetic = false;
        if (reference instanceof ExplicitConstructorCall) {
            ExplicitConstructorCall call = (ExplicitConstructorCall) reference;
            isSynthetic = call.isImplicitSuper();
            constructorBinding = call.binding;
        } else if (reference instanceof AllocationExpression) {
            AllocationExpression alloc = (AllocationExpression) reference;
            constructorBinding = alloc.binding;
        } else if (reference instanceof TypeDeclaration || reference instanceof FieldDeclaration) {
            super.matchReportReference(reference, element, elementBinding, accuracy, locator);
            if (this.match != null)
                return;
        }

        // Create search match
        this.match = locator.newMethodReferenceMatch(element, elementBinding, accuracy, -1, -1, true, isSynthetic,
                reference);

        // Look to refine accuracy
        if (constructorBinding instanceof ParameterizedGenericMethodBinding) { // parameterized generic method
            // Update match regarding constructor type arguments
            ParameterizedGenericMethodBinding parameterizedMethodBinding = (ParameterizedGenericMethodBinding) constructorBinding;
            this.match.setRaw(parameterizedMethodBinding.isRaw);
            TypeBinding[] typeBindings = parameterizedMethodBinding.isRaw ? null
                    : parameterizedMethodBinding.typeArguments;
            updateMatch(typeBindings, locator, this.pattern.constructorArguments,
                    this.pattern.hasConstructorParameters());

            // Update match regarding declaring class type arguments
            if (constructorBinding.declaringClass.isParameterizedType()
                    || constructorBinding.declaringClass.isRawType()) {
                ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding) constructorBinding.declaringClass;
                if (!this.pattern.hasTypeArguments() && this.pattern.hasConstructorArguments()
                        || parameterizedBinding.isParameterizedWithOwnVariables()) {
                    // special case for constructor pattern which defines arguments but no type
                    // in this case, we only use refined accuracy for constructor
                } else if (this.pattern.hasTypeArguments() && !this.pattern.hasConstructorArguments()) {
                    // special case for constructor pattern which defines no constructor arguments but has type ones
                    // in this case, we do not use refined accuracy
                    updateMatch(parameterizedBinding, this.pattern.getTypeArguments(),
                            this.pattern.hasTypeParameters(), 0, locator);
                } else {
                    updateMatch(parameterizedBinding, this.pattern.getTypeArguments(),
                            this.pattern.hasTypeParameters(), 0, locator);
                }
            } else if (this.pattern.hasTypeArguments()) {
                this.match.setRule(SearchPattern.R_ERASURE_MATCH);
            }

            // Update match regarding constructor parameters
            // TODO ? (frederic)
        } else if (constructorBinding instanceof ParameterizedMethodBinding) {
            // Update match regarding declaring class type arguments
            if (constructorBinding.declaringClass.isParameterizedType()
                    || constructorBinding.declaringClass.isRawType()) {
                ParameterizedTypeBinding parameterizedBinding = (ParameterizedTypeBinding) constructorBinding.declaringClass;
                if (!this.pattern.hasTypeArguments() && this.pattern.hasConstructorArguments()) {
                    // special case for constructor pattern which defines arguments but no type
                    updateMatch(parameterizedBinding, new char[][][] { this.pattern.constructorArguments },
                            this.pattern.hasTypeParameters(), 0, locator);
                } else if (!parameterizedBinding.isParameterizedWithOwnVariables()) {
                    updateMatch(parameterizedBinding, this.pattern.getTypeArguments(),
                            this.pattern.hasTypeParameters(), 0, locator);
                }
            } else if (this.pattern.hasTypeArguments()) {
                this.match.setRule(SearchPattern.R_ERASURE_MATCH);
            }

            // Update match regarding constructor parameters
            // TODO ? (frederic)
        } else if (this.pattern.hasConstructorArguments()) { // binding has no type params, compatible erasure if pattern does
            this.match.setRule(SearchPattern.R_ERASURE_MATCH);
        }

        // See whether it is necessary to report or not
        if (this.match.getRule() == 0)
            return; // impossible match
        boolean report = (this.isErasureMatch && this.match.isErasure())
                || (this.isEquivalentMatch && this.match.isEquivalent()) || this.match.isExact();
        if (!report)
            return;

        // Report match
        int offset = reference.sourceStart;
        this.match.setOffset(offset);
        this.match.setLength(reference.sourceEnd - offset + 1);
        if (reference instanceof FieldDeclaration) { // enum declaration
            FieldDeclaration enumConstant = (FieldDeclaration) reference;
            if (enumConstant.initialization instanceof QualifiedAllocationExpression) {
                locator.reportAccurateEnumConstructorReference(this.match, enumConstant,
                        (QualifiedAllocationExpression) enumConstant.initialization);
                return;
            }
        }
        locator.report(this.match);
    }

    public SearchMatch newDeclarationMatch(ASTNode reference, IJavaElement element, Binding binding, int accuracy,
            int length, MatchLocator locator) {
        this.match = null;
        int offset = reference.sourceStart;
        if (this.pattern.findReferences) {
            if (reference instanceof TypeDeclaration) {
                TypeDeclaration type = (TypeDeclaration) reference;
                AbstractMethodDeclaration[] methods = type.methods;
                if (methods != null) {
                    for (int i = 0, max = methods.length; i < max; i++) {
                        AbstractMethodDeclaration method = methods[i];
                        boolean synthetic = method.isDefaultConstructor() && method.sourceStart < type.bodyStart;
                        this.match = locator.newMethodReferenceMatch(element, binding, accuracy, offset, length,
                                method.isConstructor(), synthetic, method);
                    }
                }
            } else if (reference instanceof ConstructorDeclaration) {
                ConstructorDeclaration constructor = (ConstructorDeclaration) reference;
                ExplicitConstructorCall call = constructor.constructorCall;
                boolean synthetic = call != null && call.isImplicitSuper();
                this.match = locator.newMethodReferenceMatch(element, binding, accuracy, offset, length,
                        constructor.isConstructor(), synthetic, constructor);
            }
        }
        if (this.match != null) {
            return this.match;
        }
        // super implementation...
        return locator.newDeclarationMatch(element, binding, accuracy, reference.sourceStart, length);
    }

    public int resolveLevel(ASTNode node) {
        if (this.pattern.findReferences) {
            if (node instanceof AllocationExpression)
                return resolveLevel((AllocationExpression) node);
            if (node instanceof ExplicitConstructorCall)
                return resolveLevel(((ExplicitConstructorCall) node).binding);
            if (node instanceof TypeDeclaration)
                return resolveLevel((TypeDeclaration) node);
            if (node instanceof FieldDeclaration)
                return resolveLevel((FieldDeclaration) node);
            if (node instanceof JavadocMessageSend) {
                return resolveLevel(((JavadocMessageSend) node).binding);
            }
            if (node instanceof ReferenceExpression) {
                return resolveLevel(((ReferenceExpression) node).getMethodBinding());
            }
        }
        if (node instanceof ConstructorDeclaration)
            return resolveLevel((ConstructorDeclaration) node, true);
        return IMPOSSIBLE_MATCH;
    }

    protected int referenceType() {
        return IJavaElement.METHOD;
    }

    protected int resolveLevel(AllocationExpression allocation) {
        // constructor name is simple type name
        char[][] typeName = allocation.type.getTypeName();
        if (this.pattern.declaringSimpleName != null
                && !matchesName(this.pattern.declaringSimpleName, typeName[typeName.length - 1]))
            return IMPOSSIBLE_MATCH;

        return resolveLevel(allocation.binding);
    }

    protected int resolveLevel(FieldDeclaration field) {
        // only accept enum constants
        if (field.type != null || field.binding == null)
            return IMPOSSIBLE_MATCH;
        if (this.pattern.declaringSimpleName != null
                && !matchesName(this.pattern.declaringSimpleName, field.binding.type.sourceName()))
            return IMPOSSIBLE_MATCH;
        if (!(field.initialization instanceof AllocationExpression)
                || field.initialization.resolvedType.isLocalType())
            return IMPOSSIBLE_MATCH;

        return resolveLevel(((AllocationExpression) field.initialization).binding);
    }

    public int resolveLevel(Binding binding) {
        if (binding == null)
            return INACCURATE_MATCH;
        if (!(binding instanceof MethodBinding))
            return IMPOSSIBLE_MATCH;

        MethodBinding constructor = (MethodBinding) binding;
        int level = matchConstructor(constructor);
        if (level == IMPOSSIBLE_MATCH) {
            if (constructor != constructor.original()) {
                level = matchConstructor(constructor.original());
            }
        }
        return level;
    }

    protected int resolveLevel(ConstructorDeclaration constructor, boolean checkDeclarations) {
        int referencesLevel = IMPOSSIBLE_MATCH;
        if (this.pattern.findReferences) {
            ExplicitConstructorCall constructorCall = constructor.constructorCall;
            if (constructorCall != null && constructorCall.accessMode == ExplicitConstructorCall.ImplicitSuper) {
                // eliminate explicit super call as it will be treated with matchLevel(ExplicitConstructorCall, boolean)
                int callCount = (constructorCall.arguments == null) ? 0 : constructorCall.arguments.length;
                int patternCount = (this.pattern.parameterSimpleNames == null) ? 0
                        : this.pattern.parameterSimpleNames.length;
                if (patternCount != callCount) {
                    referencesLevel = IMPOSSIBLE_MATCH;
                } else {
                    referencesLevel = resolveLevel(constructorCall.binding);
                    if (referencesLevel == ACCURATE_MATCH)
                        return ACCURATE_MATCH; // cannot get better
                }
            }
        }
        if (!checkDeclarations)
            return referencesLevel;

        int declarationsLevel = this.pattern.findDeclarations ? resolveLevel(constructor.binding)
                : IMPOSSIBLE_MATCH;
        return referencesLevel >= declarationsLevel ? referencesLevel : declarationsLevel; // answer the stronger match
    }

    protected int resolveLevel(TypeDeclaration type) {
        // find default constructor
        AbstractMethodDeclaration[] methods = type.methods;
        if (methods != null) {
            for (int i = 0, length = methods.length; i < length; i++) {
                AbstractMethodDeclaration method = methods[i];
                if (method.isDefaultConstructor() && method.sourceStart < type.bodyStart) // if synthetic
                    return resolveLevel((ConstructorDeclaration) method, false);
            }
        }
        return IMPOSSIBLE_MATCH;
    }

    public String toString() {
        return "Locator for " + this.pattern.toString(); //$NON-NLS-1$
    }
}