org.eclipse.jdt.internal.compiler.SourceElementNotifier.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.SourceElementNotifier.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler;

import java.util.ArrayList;
import java.util.Map;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ISourceElementRequestor.ParameterInfo;
import org.eclipse.jdt.internal.compiler.ISourceElementRequestor.TypeParameterInfo;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.ExportsStatement;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
import org.eclipse.jdt.internal.compiler.ast.OpensStatement;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;

@SuppressWarnings({ "rawtypes", "unchecked" })
public class SourceElementNotifier {
    /**
     * An ast visitor that visits local type declarations.
     */
    public class LocalDeclarationVisitor extends ASTVisitor {
        public ImportReference currentPackage;
        ArrayList declaringTypes;

        public void pushDeclaringType(TypeDeclaration declaringType) {
            if (this.declaringTypes == null) {
                this.declaringTypes = new ArrayList();
            }
            this.declaringTypes.add(declaringType);
        }

        public void popDeclaringType() {
            this.declaringTypes.remove(this.declaringTypes.size() - 1);
        }

        public TypeDeclaration peekDeclaringType() {
            if (this.declaringTypes == null)
                return null;
            int size = this.declaringTypes.size();
            if (size == 0)
                return null;
            return (TypeDeclaration) this.declaringTypes.get(size - 1);
        }

        @Override
        public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) {
            notifySourceElementRequestor(typeDeclaration, true, peekDeclaringType(), this.currentPackage);
            return false; // don't visit members as this was done during notifySourceElementRequestor(...)
        }

        @Override
        public boolean visit(TypeDeclaration typeDeclaration, ClassScope scope) {
            notifySourceElementRequestor(typeDeclaration, true, peekDeclaringType(), this.currentPackage);
            return false; // don't visit members as this was done during notifySourceElementRequestor(...)
        }
    }

    ISourceElementRequestor requestor;
    boolean reportReferenceInfo;
    char[][] typeNames;
    char[][] superTypeNames;
    int nestedTypeIndex;
    LocalDeclarationVisitor localDeclarationVisitor = null;

    HashtableOfObjectToInt sourceEnds;
    Map<ASTNode, char[][]> nodesToCategories;

    int initialPosition;
    int eofPosition;

    public SourceElementNotifier(ISourceElementRequestor requestor, boolean reportLocalDeclarations) {
        this.requestor = requestor;
        if (reportLocalDeclarations) {
            this.localDeclarationVisitor = new LocalDeclarationVisitor();
        }
        this.typeNames = new char[4][];
        this.superTypeNames = new char[4][];
        this.nestedTypeIndex = 0;
    }

    protected Object[][] getArgumentInfos(Argument[] arguments) {
        int argumentLength = arguments.length;
        char[][] argumentTypes = new char[argumentLength][];
        char[][] argumentNames = new char[argumentLength][];
        ParameterInfo[] parameterInfos = new ParameterInfo[argumentLength];
        for (int i = 0; i < argumentLength; i++) {
            Argument argument = arguments[i];
            argumentTypes[i] = CharOperation.concatWith(argument.type.getParameterizedTypeName(), '.');
            char[] name = argument.name;
            argumentNames[i] = name;
            ParameterInfo parameterInfo = new ParameterInfo();
            parameterInfo.declarationStart = argument.declarationSourceStart;
            parameterInfo.declarationEnd = argument.declarationSourceEnd;
            parameterInfo.nameSourceStart = argument.sourceStart;
            parameterInfo.nameSourceEnd = argument.sourceEnd;
            parameterInfo.modifiers = argument.modifiers;
            parameterInfo.name = name;
            parameterInfos[i] = parameterInfo;
        }

        return new Object[][] { parameterInfos, new char[][][] { argumentTypes, argumentNames } };
    }

    protected char[][] getInterfaceNames(TypeDeclaration typeDeclaration) {
        char[][] interfaceNames = null;
        int superInterfacesLength = 0;
        TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
        if (superInterfaces != null) {
            superInterfacesLength = superInterfaces.length;
            interfaceNames = new char[superInterfacesLength][];
        } else {
            if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
                // see PR 3442
                QualifiedAllocationExpression alloc = typeDeclaration.allocation;
                if (alloc != null && alloc.type != null) {
                    superInterfaces = new TypeReference[] { alloc.type };
                    superInterfacesLength = 1;
                    interfaceNames = new char[1][];
                }
            }
        }
        if (superInterfaces != null) {
            for (int i = 0; i < superInterfacesLength; i++) {
                interfaceNames[i] = CharOperation.concatWith(superInterfaces[i].getParameterizedTypeName(), '.');
            }
        }
        return interfaceNames;
    }

    protected char[] getSuperclassName(TypeDeclaration typeDeclaration) {
        TypeReference superclass = typeDeclaration.superclass;
        return superclass != null ? CharOperation.concatWith(superclass.getParameterizedTypeName(), '.') : null;
    }

    protected char[][] getThrownExceptions(AbstractMethodDeclaration methodDeclaration) {
        char[][] thrownExceptionTypes = null;
        TypeReference[] thrownExceptions = methodDeclaration.thrownExceptions;
        if (thrownExceptions != null) {
            int thrownExceptionLength = thrownExceptions.length;
            thrownExceptionTypes = new char[thrownExceptionLength][];
            for (int i = 0; i < thrownExceptionLength; i++) {
                thrownExceptionTypes[i] = CharOperation.concatWith(thrownExceptions[i].getParameterizedTypeName(),
                        '.');
            }
        }
        return thrownExceptionTypes;
    }

    protected char[][] getTypeParameterBounds(TypeParameter typeParameter) {
        TypeReference firstBound = typeParameter.type;
        TypeReference[] otherBounds = typeParameter.bounds;
        char[][] typeParameterBounds = null;
        if (firstBound != null) {
            if (otherBounds != null) {
                int otherBoundsLength = otherBounds.length;
                char[][] boundNames = new char[otherBoundsLength + 1][];
                boundNames[0] = CharOperation.concatWith(firstBound.getParameterizedTypeName(), '.');
                for (int j = 0; j < otherBoundsLength; j++) {
                    boundNames[j + 1] = CharOperation.concatWith(otherBounds[j].getParameterizedTypeName(), '.');
                }
                typeParameterBounds = boundNames;
            } else {
                typeParameterBounds = new char[][] {
                        CharOperation.concatWith(firstBound.getParameterizedTypeName(), '.') };
            }
        } else {
            typeParameterBounds = CharOperation.NO_CHAR_CHAR;
        }

        return typeParameterBounds;
    }

    private TypeParameterInfo[] getTypeParameterInfos(TypeParameter[] typeParameters) {
        if (typeParameters == null)
            return null;
        int typeParametersLength = typeParameters.length;
        TypeParameterInfo[] result = new TypeParameterInfo[typeParametersLength];
        for (int i = 0; i < typeParametersLength; i++) {
            TypeParameter typeParameter = typeParameters[i];
            char[][] typeParameterBounds = getTypeParameterBounds(typeParameter);
            ISourceElementRequestor.TypeParameterInfo typeParameterInfo = new ISourceElementRequestor.TypeParameterInfo();
            typeParameterInfo.typeAnnotated = ((typeParameter.bits & ASTNode.HasTypeAnnotations) != 0);
            typeParameterInfo.declarationStart = typeParameter.declarationSourceStart;
            typeParameterInfo.declarationEnd = typeParameter.declarationSourceEnd;
            typeParameterInfo.name = typeParameter.name;
            typeParameterInfo.nameSourceStart = typeParameter.sourceStart;
            typeParameterInfo.nameSourceEnd = typeParameter.sourceEnd;
            typeParameterInfo.bounds = typeParameterBounds;
            result[i] = typeParameterInfo;
        }
        return result;
    }

    /*
     * Checks whether one of the annotations is the @Deprecated annotation
     * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=89807)
     */
    private boolean hasDeprecatedAnnotation(Annotation[] annotations) {
        if (annotations != null) {
            for (int i = 0, length = annotations.length; i < length; i++) {
                Annotation annotation = annotations[i];
                if (CharOperation.equals(annotation.type.getLastToken(), TypeConstants.JAVA_LANG_DEPRECATED[2])) {
                    return true;
                }
            }
        }
        return false;
    }

    /*
     * Update the bodyStart of the corresponding parse node
     */
    protected void notifySourceElementRequestor(AbstractMethodDeclaration methodDeclaration,
            TypeDeclaration declaringType, ImportReference currentPackage) {

        // range check
        boolean isInRange = this.initialPosition <= methodDeclaration.declarationSourceStart
                && this.eofPosition >= methodDeclaration.declarationSourceEnd;

        if (methodDeclaration.isClinit()) {
            this.visitIfNeeded(methodDeclaration);
            return;
        }

        if (methodDeclaration.isDefaultConstructor()) {
            if (this.reportReferenceInfo) {
                ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) methodDeclaration;
                ExplicitConstructorCall constructorCall = constructorDeclaration.constructorCall;
                if (constructorCall != null) {
                    switch (constructorCall.accessMode) {
                    case ExplicitConstructorCall.This:
                        this.requestor.acceptConstructorReference(this.typeNames[this.nestedTypeIndex - 1],
                                constructorCall.arguments == null ? 0 : constructorCall.arguments.length,
                                constructorCall.sourceStart);
                        break;
                    case ExplicitConstructorCall.Super:
                    case ExplicitConstructorCall.ImplicitSuper:
                        this.requestor.acceptConstructorReference(this.superTypeNames[this.nestedTypeIndex - 1],
                                constructorCall.arguments == null ? 0 : constructorCall.arguments.length,
                                constructorCall.sourceStart);
                        break;
                    }
                }
            }
            return;
        }
        char[][] argumentTypes = null;
        char[][] argumentNames = null;
        boolean isVarArgs = false;
        Argument[] arguments = methodDeclaration.arguments;
        ParameterInfo[] parameterInfos = null;
        ISourceElementRequestor.MethodInfo methodInfo = new ISourceElementRequestor.MethodInfo();
        methodInfo.typeAnnotated = ((methodDeclaration.bits & ASTNode.HasTypeAnnotations) != 0);

        if (arguments != null) {
            Object[][] argumentInfos = getArgumentInfos(arguments);
            parameterInfos = (ParameterInfo[]) argumentInfos[0];
            argumentTypes = (char[][]) argumentInfos[1][0];
            argumentNames = (char[][]) argumentInfos[1][1];

            isVarArgs = arguments[arguments.length - 1].isVarArgs();
        }
        char[][] thrownExceptionTypes = getThrownExceptions(methodDeclaration);
        // by default no selector end position
        int selectorSourceEnd = -1;
        if (methodDeclaration.isConstructor()) {
            selectorSourceEnd = this.sourceEnds.get(methodDeclaration);
            if (isInRange) {
                int currentModifiers = methodDeclaration.modifiers;
                currentModifiers &= ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccDeprecated;
                if (isVarArgs)
                    currentModifiers |= ClassFileConstants.AccVarargs;
                if (hasDeprecatedAnnotation(methodDeclaration.annotations))
                    currentModifiers |= ClassFileConstants.AccDeprecated;

                methodInfo.isConstructor = true;
                methodInfo.declarationStart = methodDeclaration.declarationSourceStart;
                methodInfo.modifiers = currentModifiers;
                methodInfo.name = methodDeclaration.selector;
                methodInfo.nameSourceStart = methodDeclaration.sourceStart;
                methodInfo.nameSourceEnd = selectorSourceEnd;
                methodInfo.parameterTypes = argumentTypes;
                methodInfo.parameterNames = argumentNames;
                methodInfo.exceptionTypes = thrownExceptionTypes;
                methodInfo.typeParameters = getTypeParameterInfos(methodDeclaration.typeParameters());
                methodInfo.parameterInfos = parameterInfos;
                methodInfo.categories = this.nodesToCategories.get(methodDeclaration);
                methodInfo.annotations = methodDeclaration.annotations;
                methodInfo.declaringPackageName = currentPackage == null ? CharOperation.NO_CHAR
                        : CharOperation.concatWith(currentPackage.tokens, '.');
                methodInfo.declaringTypeModifiers = declaringType.modifiers;
                methodInfo.extraFlags = ExtraFlags.getExtraFlags(declaringType);
                methodInfo.node = methodDeclaration;
                this.requestor.enterConstructor(methodInfo);
            }
            if (this.reportReferenceInfo) {
                ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) methodDeclaration;
                ExplicitConstructorCall constructorCall = constructorDeclaration.constructorCall;
                if (constructorCall != null) {
                    switch (constructorCall.accessMode) {
                    case ExplicitConstructorCall.This:
                        this.requestor.acceptConstructorReference(this.typeNames[this.nestedTypeIndex - 1],
                                constructorCall.arguments == null ? 0 : constructorCall.arguments.length,
                                constructorCall.sourceStart);
                        break;
                    case ExplicitConstructorCall.Super:
                    case ExplicitConstructorCall.ImplicitSuper:
                        this.requestor.acceptConstructorReference(this.superTypeNames[this.nestedTypeIndex - 1],
                                constructorCall.arguments == null ? 0 : constructorCall.arguments.length,
                                constructorCall.sourceStart);
                        break;
                    }
                }
            }
            this.visitIfNeeded(methodDeclaration);
            if (isInRange) {
                this.requestor.exitConstructor(methodDeclaration.declarationSourceEnd);
            }
            return;
        }
        selectorSourceEnd = this.sourceEnds.get(methodDeclaration);
        if (isInRange) {
            int currentModifiers = methodDeclaration.modifiers;
            currentModifiers &= ExtraCompilerModifiers.AccJustFlag | ClassFileConstants.AccDeprecated
                    | ClassFileConstants.AccAnnotationDefault | ExtraCompilerModifiers.AccDefaultMethod;
            if (isVarArgs)
                currentModifiers |= ClassFileConstants.AccVarargs;
            if (hasDeprecatedAnnotation(methodDeclaration.annotations))
                currentModifiers |= ClassFileConstants.AccDeprecated;

            TypeReference returnType = methodDeclaration instanceof MethodDeclaration
                    ? ((MethodDeclaration) methodDeclaration).returnType
                    : null;
            methodInfo.isAnnotation = methodDeclaration instanceof AnnotationMethodDeclaration;
            methodInfo.declarationStart = methodDeclaration.declarationSourceStart;
            methodInfo.modifiers = currentModifiers;
            methodInfo.returnType = returnType == null ? null
                    : CharOperation.concatWith(returnType.getParameterizedTypeName(), '.');
            methodInfo.name = methodDeclaration.selector;
            methodInfo.nameSourceStart = methodDeclaration.sourceStart;
            methodInfo.nameSourceEnd = selectorSourceEnd;
            methodInfo.parameterTypes = argumentTypes;
            methodInfo.parameterNames = argumentNames;
            methodInfo.exceptionTypes = thrownExceptionTypes;
            methodInfo.typeParameters = getTypeParameterInfos(methodDeclaration.typeParameters());
            methodInfo.parameterInfos = parameterInfos;
            methodInfo.categories = this.nodesToCategories.get(methodDeclaration);
            methodInfo.annotations = methodDeclaration.annotations;
            methodInfo.node = methodDeclaration;
            methodInfo.enclosingType = declaringType;
            methodInfo.declaringPackageName = currentPackage == null ? CharOperation.NO_CHAR
                    : CharOperation.concatWith(currentPackage.tokens, '.');
            this.requestor.enterMethod(methodInfo);
        }

        this.visitIfNeeded(methodDeclaration);

        if (isInRange) {
            if (methodDeclaration instanceof AnnotationMethodDeclaration) {
                AnnotationMethodDeclaration annotationMethodDeclaration = (AnnotationMethodDeclaration) methodDeclaration;
                Expression expression = annotationMethodDeclaration.defaultValue;
                if (expression != null) {
                    this.requestor.exitMethod(methodDeclaration.declarationSourceEnd, expression);
                    return;
                }
            }
            this.requestor.exitMethod(methodDeclaration.declarationSourceEnd, null);
        }
    }

    /*
     * Update the bodyStart of the corresponding parse node
     */
    public void notifySourceElementRequestor(CompilationUnitDeclaration parsedUnit, int sourceStart, int sourceEnd,
            boolean reportReference, HashtableOfObjectToInt sourceEndsMap, Map nodesToCategoriesMap) {

        this.initialPosition = sourceStart;
        this.eofPosition = sourceEnd;

        this.reportReferenceInfo = reportReference;
        this.sourceEnds = sourceEndsMap;
        this.nodesToCategories = nodesToCategoriesMap;

        try {
            // range check
            boolean isInRange = this.initialPosition <= parsedUnit.sourceStart
                    && this.eofPosition >= parsedUnit.sourceEnd;

            // collect the top level ast nodes
            int length = 0;
            ASTNode[] nodes = null;
            if (isInRange) {
                this.requestor.enterCompilationUnit();
            }
            ImportReference currentPackage = parsedUnit.currentPackage;
            if (this.localDeclarationVisitor != null) {
                this.localDeclarationVisitor.currentPackage = currentPackage;
            }
            ImportReference[] imports = parsedUnit.imports;
            TypeDeclaration[] types = parsedUnit.types;
            length = (currentPackage == null ? 0 : 1) + (imports == null ? 0 : imports.length)
                    + (types == null ? 0 : types.length) + (parsedUnit.moduleDeclaration == null ? 0 : 1);
            nodes = new ASTNode[length];
            int index = 0;
            if (currentPackage != null) {
                nodes[index++] = currentPackage;
            }
            if (imports != null) {
                for (int i = 0, max = imports.length; i < max; i++) {
                    nodes[index++] = imports[i];
                }
            }
            if (types != null) {
                for (int i = 0, max = types.length; i < max; i++) {
                    nodes[index++] = types[i];
                }
            }

            if (parsedUnit.moduleDeclaration != null)
                nodes[index++] = parsedUnit.moduleDeclaration;

            // notify the nodes in the syntactical order
            if (length > 0) {
                quickSort(nodes, 0, length - 1);
                for (int i = 0; i < length; i++) {
                    ASTNode node = nodes[i];
                    if (node instanceof ImportReference) {
                        ImportReference importRef = (ImportReference) node;
                        if (node == parsedUnit.currentPackage) {
                            notifySourceElementRequestor(importRef, true);
                        } else {
                            notifySourceElementRequestor(importRef, false);
                        }
                    } else if (node instanceof TypeDeclaration) {
                        notifySourceElementRequestor((TypeDeclaration) node, true, null, currentPackage);
                    } else if (node instanceof ModuleDeclaration) {
                        notifySourceElementRequestor(parsedUnit.moduleDeclaration);
                    }
                }
            }

            if (isInRange) {
                this.requestor.exitCompilationUnit(parsedUnit.sourceEnd);
            }
        } finally {
            reset();
        }
    }

    /*
    * Update the bodyStart of the corresponding parse node
    */
    protected void notifySourceElementRequestor(FieldDeclaration fieldDeclaration, TypeDeclaration declaringType) {

        // range check
        boolean isInRange = this.initialPosition <= fieldDeclaration.declarationSourceStart
                && this.eofPosition >= fieldDeclaration.declarationSourceEnd;

        switch (fieldDeclaration.getKind()) {
        case AbstractVariableDeclaration.ENUM_CONSTANT:
            if (this.reportReferenceInfo) {
                // accept constructor reference for enum constant
                if (fieldDeclaration.initialization instanceof AllocationExpression) {
                    AllocationExpression alloc = (AllocationExpression) fieldDeclaration.initialization;
                    this.requestor.acceptConstructorReference(declaringType.name,
                            alloc.arguments == null ? 0 : alloc.arguments.length, alloc.sourceStart);
                }
            }
            // $FALL-THROUGH$
        case AbstractVariableDeclaration.FIELD:
            int fieldEndPosition = this.sourceEnds.get(fieldDeclaration);
            if (fieldEndPosition == -1) {
                // use the declaration source end by default
                fieldEndPosition = fieldDeclaration.declarationSourceEnd;
            }
            if (isInRange) {
                int currentModifiers = fieldDeclaration.modifiers;

                // remember deprecation so as to not lose it below
                boolean deprecated = (currentModifiers & ClassFileConstants.AccDeprecated) != 0
                        || hasDeprecatedAnnotation(fieldDeclaration.annotations);

                char[] typeName = null;
                if (fieldDeclaration.type == null) {
                    // enum constant
                    typeName = declaringType.name;
                    currentModifiers |= ClassFileConstants.AccEnum;
                } else {
                    // regular field
                    typeName = CharOperation.concatWith(fieldDeclaration.type.getParameterizedTypeName(), '.');
                }
                ISourceElementRequestor.FieldInfo fieldInfo = new ISourceElementRequestor.FieldInfo();
                fieldInfo.typeAnnotated = ((fieldDeclaration.bits & ASTNode.HasTypeAnnotations) != 0);
                fieldInfo.declarationStart = fieldDeclaration.declarationSourceStart;
                fieldInfo.name = fieldDeclaration.name;
                fieldInfo.modifiers = deprecated
                        ? (currentModifiers & ExtraCompilerModifiers.AccJustFlag) | ClassFileConstants.AccDeprecated
                        : currentModifiers & ExtraCompilerModifiers.AccJustFlag;
                fieldInfo.type = typeName;
                fieldInfo.nameSourceStart = fieldDeclaration.sourceStart;
                fieldInfo.nameSourceEnd = fieldDeclaration.sourceEnd;
                fieldInfo.categories = this.nodesToCategories.get(fieldDeclaration);
                fieldInfo.annotations = fieldDeclaration.annotations;
                fieldInfo.node = fieldDeclaration;
                this.requestor.enterField(fieldInfo);
            }
            this.visitIfNeeded(fieldDeclaration, declaringType);
            if (isInRange) {
                this.requestor.exitField(
                        // filter out initializations that are not a constant (simple check)
                        (fieldDeclaration.initialization == null
                                || fieldDeclaration.initialization instanceof ArrayInitializer
                                || fieldDeclaration.initialization instanceof AllocationExpression
                                || fieldDeclaration.initialization instanceof ArrayAllocationExpression
                                || fieldDeclaration.initialization instanceof Assignment
                                || fieldDeclaration.initialization instanceof ClassLiteralAccess
                                || fieldDeclaration.initialization instanceof MessageSend
                                || fieldDeclaration.initialization instanceof ArrayReference
                                || fieldDeclaration.initialization instanceof ThisReference) ? -1
                                        : fieldDeclaration.initialization.sourceStart,
                        fieldEndPosition, fieldDeclaration.declarationSourceEnd);
            }
            break;
        case AbstractVariableDeclaration.INITIALIZER:
            if (isInRange) {
                this.requestor.enterInitializer(fieldDeclaration.declarationSourceStart,
                        fieldDeclaration.modifiers);
            }
            this.visitIfNeeded((Initializer) fieldDeclaration);
            if (isInRange) {
                this.requestor.exitInitializer(fieldDeclaration.declarationSourceEnd);
            }
            break;
        }
    }

    protected void notifySourceElementRequestor(ImportReference importReference, boolean isPackage) {
        if (isPackage) {
            this.requestor.acceptPackage(importReference);
        } else {
            final boolean onDemand = (importReference.bits & ASTNode.OnDemand) != 0;
            this.requestor.acceptImport(importReference.declarationSourceStart,
                    importReference.declarationSourceEnd, importReference.sourceStart,
                    onDemand ? importReference.trailingStarPosition : importReference.sourceEnd,
                    importReference.tokens, onDemand, importReference.modifiers);
        }
    }

    protected void notifySourceElementRequestor(ModuleDeclaration moduleDeclaration) {
        boolean isInRange = this.initialPosition <= moduleDeclaration.declarationSourceStart
                && this.eofPosition >= moduleDeclaration.declarationSourceEnd;
        ISourceElementRequestor.ModuleInfo info = new ISourceElementRequestor.ModuleInfo();
        if (isInRange) {

            int currentModifiers = moduleDeclaration.modifiers;

            // remember deprecation so as to not lose it below
            boolean deprecated = (currentModifiers & ClassFileConstants.AccDeprecated) != 0
                    || hasDeprecatedAnnotation(moduleDeclaration.annotations);

            info.declarationStart = moduleDeclaration.declarationSourceStart;
            info.modifiers = deprecated
                    ? (currentModifiers & ExtraCompilerModifiers.AccJustFlag) | ClassFileConstants.AccDeprecated
                    : currentModifiers & ExtraCompilerModifiers.AccJustFlag;
            info.name = TypeConstants.MODULE_INFO_NAME;
            info.nameSourceStart = moduleDeclaration.sourceStart;
            info.nameSourceEnd = moduleDeclaration.sourceEnd;
            info.moduleName = moduleDeclaration.moduleName;
            info.annotations = moduleDeclaration.annotations;
            info.node = moduleDeclaration;
            info.categories = this.nodesToCategories.get(moduleDeclaration);
            fillModuleInfo(moduleDeclaration, info);
            this.requestor.enterModule(info);
            this.requestor.exitModule(moduleDeclaration.declarationSourceEnd);
        }
    }

    protected void notifySourceElementRequestor(TypeDeclaration typeDeclaration, boolean notifyTypePresence,
            TypeDeclaration declaringType, ImportReference currentPackage) {

        if (CharOperation.equals(TypeConstants.PACKAGE_INFO_NAME, typeDeclaration.name))
            return;

        // range check
        boolean isInRange = this.initialPosition <= typeDeclaration.declarationSourceStart
                && this.eofPosition >= typeDeclaration.declarationSourceEnd;

        FieldDeclaration[] fields = typeDeclaration.fields;
        AbstractMethodDeclaration[] methods = typeDeclaration.methods;
        TypeDeclaration[] memberTypes = typeDeclaration.memberTypes;
        int fieldCounter = fields == null ? 0 : fields.length;
        int methodCounter = methods == null ? 0 : methods.length;
        int memberTypeCounter = memberTypes == null ? 0 : memberTypes.length;
        int fieldIndex = 0;
        int methodIndex = 0;
        int memberTypeIndex = 0;

        if (notifyTypePresence) {
            char[][] interfaceNames = getInterfaceNames(typeDeclaration);
            int kind = TypeDeclaration.kind(typeDeclaration.modifiers);
            char[] implicitSuperclassName = TypeConstants.CharArray_JAVA_LANG_OBJECT;
            ISourceElementRequestor.TypeInfo typeInfo = new ISourceElementRequestor.TypeInfo();
            typeInfo.typeAnnotated = ((typeDeclaration.bits & ASTNode.HasTypeAnnotations) != 0);
            if (isInRange) {
                int currentModifiers = typeDeclaration.modifiers;

                // remember deprecation so as to not lose it below
                boolean deprecated = (currentModifiers & ClassFileConstants.AccDeprecated) != 0
                        || hasDeprecatedAnnotation(typeDeclaration.annotations);

                boolean isEnumInit = typeDeclaration.allocation != null
                        && typeDeclaration.allocation.enumConstant != null;
                char[] superclassName;
                if (isEnumInit) {
                    currentModifiers |= ClassFileConstants.AccEnum;
                    superclassName = declaringType.name;
                } else {
                    superclassName = getSuperclassName(typeDeclaration);
                }
                if (typeDeclaration.allocation == null) {
                    typeInfo.declarationStart = typeDeclaration.declarationSourceStart;
                } else if (isEnumInit) {
                    typeInfo.declarationStart = typeDeclaration.allocation.enumConstant.sourceStart;
                } else {
                    typeInfo.declarationStart = typeDeclaration.allocation.sourceStart;
                }
                typeInfo.modifiers = deprecated
                        ? (currentModifiers & ExtraCompilerModifiers.AccJustFlag) | ClassFileConstants.AccDeprecated
                        : currentModifiers & ExtraCompilerModifiers.AccJustFlag;
                typeInfo.name = typeDeclaration.name;
                typeInfo.nameSourceStart = isEnumInit ? typeDeclaration.allocation.enumConstant.sourceStart
                        : typeDeclaration.sourceStart;
                typeInfo.nameSourceEnd = sourceEnd(typeDeclaration);
                typeInfo.superclass = superclassName;
                typeInfo.superinterfaces = interfaceNames;
                typeInfo.typeParameters = getTypeParameterInfos(typeDeclaration.typeParameters);
                typeInfo.categories = this.nodesToCategories.get(typeDeclaration);
                typeInfo.secondary = typeDeclaration.isSecondary();
                typeInfo.anonymousMember = typeDeclaration.allocation != null
                        && typeDeclaration.allocation.enclosingInstance != null;
                typeInfo.annotations = typeDeclaration.annotations;
                typeInfo.extraFlags = ExtraFlags.getExtraFlags(typeDeclaration);
                typeInfo.node = typeDeclaration;
                this.requestor.enterType(typeInfo);
                switch (kind) {
                case TypeDeclaration.CLASS_DECL:
                    if (superclassName != null)
                        implicitSuperclassName = superclassName;
                    break;
                case TypeDeclaration.INTERFACE_DECL:
                    implicitSuperclassName = TypeConstants.CharArray_JAVA_LANG_OBJECT;
                    break;
                case TypeDeclaration.ENUM_DECL:
                    implicitSuperclassName = TypeConstants.CharArray_JAVA_LANG_ENUM;
                    break;
                case TypeDeclaration.ANNOTATION_TYPE_DECL:
                    implicitSuperclassName = TypeConstants.CharArray_JAVA_LANG_ANNOTATION_ANNOTATION;
                    break;
                }
            }
            if (this.nestedTypeIndex == this.typeNames.length) {
                // need a resize
                System.arraycopy(this.typeNames, 0, (this.typeNames = new char[this.nestedTypeIndex * 2][]), 0,
                        this.nestedTypeIndex);
                System.arraycopy(this.superTypeNames, 0,
                        (this.superTypeNames = new char[this.nestedTypeIndex * 2][]), 0, this.nestedTypeIndex);
            }
            this.typeNames[this.nestedTypeIndex] = typeDeclaration.name;
            this.superTypeNames[this.nestedTypeIndex++] = implicitSuperclassName;
        }
        while ((fieldIndex < fieldCounter) || (memberTypeIndex < memberTypeCounter)
                || (methodIndex < methodCounter)) {
            FieldDeclaration nextFieldDeclaration = null;
            AbstractMethodDeclaration nextMethodDeclaration = null;
            TypeDeclaration nextMemberDeclaration = null;

            int position = Integer.MAX_VALUE;
            int nextDeclarationType = -1;
            if (fieldIndex < fieldCounter) {
                nextFieldDeclaration = fields[fieldIndex];
                if (nextFieldDeclaration.declarationSourceStart < position) {
                    position = nextFieldDeclaration.declarationSourceStart;
                    nextDeclarationType = 0; // FIELD
                }
            }
            if (methodIndex < methodCounter) {
                nextMethodDeclaration = methods[methodIndex];
                if (nextMethodDeclaration.declarationSourceStart < position) {
                    position = nextMethodDeclaration.declarationSourceStart;
                    nextDeclarationType = 1; // METHOD
                }
            }
            if (memberTypeIndex < memberTypeCounter) {
                nextMemberDeclaration = memberTypes[memberTypeIndex];
                if (nextMemberDeclaration.declarationSourceStart < position) {
                    position = nextMemberDeclaration.declarationSourceStart;
                    nextDeclarationType = 2; // MEMBER
                }
            }
            switch (nextDeclarationType) {
            case 0:
                fieldIndex++;
                notifySourceElementRequestor(nextFieldDeclaration, typeDeclaration);
                break;
            case 1:
                methodIndex++;
                notifySourceElementRequestor(nextMethodDeclaration, typeDeclaration, currentPackage);
                break;
            case 2:
                memberTypeIndex++;
                notifySourceElementRequestor(nextMemberDeclaration, true, null, currentPackage);
            }
        }
        if (notifyTypePresence) {
            if (isInRange) {
                this.requestor.exitType(typeDeclaration.declarationSourceEnd);
            }
            this.nestedTypeIndex--;
        }
    }

    private void fillModuleInfo(ModuleDeclaration mod, ISourceElementRequestor.ModuleInfo modInfo) {
        if (mod.requiresCount > 0) {
            ISourceElementRequestor.RequiresInfo reqs[] = new ISourceElementRequestor.RequiresInfo[mod.requiresCount];
            for (int i = 0; i < mod.requiresCount; i++) {
                ISourceElementRequestor.RequiresInfo req = new ISourceElementRequestor.RequiresInfo();
                req.moduleName = CharOperation.concatWith(mod.requires[i].module.tokens, '.');
                req.modifiers = mod.requires[i].modifiers;
                reqs[i] = req;
            }
            modInfo.requires = reqs;
        }
        if (mod.exportsCount > 0) {
            ISourceElementRequestor.PackageExportInfo exps[] = new ISourceElementRequestor.PackageExportInfo[mod.exportsCount];
            for (int i = 0; i < mod.exportsCount; i++) {
                ISourceElementRequestor.PackageExportInfo exp = new ISourceElementRequestor.PackageExportInfo();
                ExportsStatement exportsStatement = mod.exports[i];
                exp.pkgName = exportsStatement.pkgName;
                if (exportsStatement.targets == null) {
                    exp.targets = CharOperation.NO_CHAR_CHAR;
                } else {
                    exp.targets = new char[exportsStatement.targets.length][];
                    for (int j = 0; j < exp.targets.length; j++) {
                        exp.targets[j] = CharOperation.concatWith(exportsStatement.targets[j].tokens, '.');
                    }
                }
                exps[i] = exp;
            }
            modInfo.exports = exps;
        }
        if (mod.servicesCount > 0) {
            ISourceElementRequestor.ServicesInfo[] services = new ISourceElementRequestor.ServicesInfo[mod.servicesCount];
            for (int i = 0; i < services.length; i++) {
                ISourceElementRequestor.ServicesInfo ser = new ISourceElementRequestor.ServicesInfo();
                ser.serviceName = CharOperation
                        .concatWith(mod.services[i].serviceInterface.getParameterizedTypeName(), '.');
                ser.implNames = new char[mod.services[i].implementations.length][];
                for (int j = 0; j < ser.implNames.length; j++) {
                    ser.implNames[j] = CharOperation
                            .concatWith(mod.services[i].implementations[j].getParameterizedTypeName(), '.');
                }
                services[i] = ser;
            }
            modInfo.services = services;
        }
        if (mod.usesCount > 0) {
            char[][] uses = new char[mod.usesCount][];
            for (int i = 0; i < uses.length; i++) {
                uses[i] = CharOperation.concatWith(mod.uses[i].serviceInterface.getParameterizedTypeName(), '.');
            }
            modInfo.usedServices = uses;
        }
        if (mod.opensCount > 0) {
            ISourceElementRequestor.PackageExportInfo opens[] = new ISourceElementRequestor.PackageExportInfo[mod.opensCount];
            for (int i = 0; i < mod.opensCount; i++) {
                ISourceElementRequestor.PackageExportInfo op = new ISourceElementRequestor.PackageExportInfo();
                OpensStatement openStmt = mod.opens[i];
                op.pkgName = openStmt.pkgName;
                if (openStmt.targets == null) {
                    op.targets = CharOperation.NO_CHAR_CHAR;
                } else {
                    op.targets = new char[openStmt.targets.length][];
                    for (int j = 0; j < op.targets.length; j++) {
                        op.targets[j] = CharOperation.concatWith(openStmt.targets[j].tokens, '.');
                    }
                }
                opens[i] = op;
            }
            modInfo.opens = opens;
        }
    }

    /*
     * Sort the given ast nodes by their positions.
     */
    private static void quickSort(ASTNode[] sortedCollection, int left, int right) {
        int original_left = left;
        int original_right = right;
        ASTNode mid = sortedCollection[left + (right - left) / 2];
        do {
            while (sortedCollection[left].sourceStart < mid.sourceStart) {
                left++;
            }
            while (mid.sourceStart < sortedCollection[right].sourceStart) {
                right--;
            }
            if (left <= right) {
                ASTNode tmp = sortedCollection[left];
                sortedCollection[left] = sortedCollection[right];
                sortedCollection[right] = tmp;
                left++;
                right--;
            }
        } while (left <= right);
        if (original_left < right) {
            quickSort(sortedCollection, original_left, right);
        }
        if (left < original_right) {
            quickSort(sortedCollection, left, original_right);
        }
    }

    private void reset() {
        this.typeNames = new char[4][];
        this.superTypeNames = new char[4][];
        this.nestedTypeIndex = 0;

        this.sourceEnds = null;
    }

    private int sourceEnd(TypeDeclaration typeDeclaration) {
        if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) {
            QualifiedAllocationExpression allocation = typeDeclaration.allocation;
            if (allocation.enumConstant != null) // case of enum constant body
                return allocation.enumConstant.sourceEnd;
            return allocation.type.sourceEnd;
        } else {
            return typeDeclaration.sourceEnd;
        }
    }

    private void visitIfNeeded(AbstractMethodDeclaration method) {
        if (this.localDeclarationVisitor != null && (method.bits & ASTNode.HasLocalType) != 0) {
            if (method instanceof ConstructorDeclaration) {
                ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) method;
                if (constructorDeclaration.constructorCall != null) {
                    constructorDeclaration.constructorCall.traverse(this.localDeclarationVisitor, method.scope);
                }
            }
            if (method.statements != null) {
                int statementsLength = method.statements.length;
                for (int i = 0; i < statementsLength; i++)
                    method.statements[i].traverse(this.localDeclarationVisitor, method.scope);
            }
        }
    }

    private void visitIfNeeded(FieldDeclaration field, TypeDeclaration declaringType) {
        if (this.localDeclarationVisitor != null && (field.bits & ASTNode.HasLocalType) != 0) {
            if (field.initialization != null) {
                try {
                    this.localDeclarationVisitor.pushDeclaringType(declaringType);
                    field.initialization.traverse(this.localDeclarationVisitor, (MethodScope) null);
                } finally {
                    this.localDeclarationVisitor.popDeclaringType();
                }
            }
        }
    }

    private void visitIfNeeded(Initializer initializer) {
        if (this.localDeclarationVisitor != null && (initializer.bits & ASTNode.HasLocalType) != 0) {
            if (initializer.block != null) {
                initializer.block.traverse(this.localDeclarationVisitor, null);
            }
        }
    }
}