org.jsweet.input.typescriptdef.ast.Scanner.java Source code

Java tutorial

Introduction

Here is the source code for org.jsweet.input.typescriptdef.ast.Scanner.java

Source

/* 
 * TypeScript definitions to Java translator - http://www.jsweet.org
 * Copyright (C) 2015 CINCHEO SAS <renaud.pawlak@cincheo.fr>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *  
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.jsweet.input.typescriptdef.ast;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.function.BiConsumer;
import java.util.function.Predicate;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.log4j.Logger;
import org.jsweet.JSweetDefTranslatorConfig;

/**
 * The root scanner for visiting an AST of TypeScript definitions.
 * 
 * @author Renaud Pawlak
 */
public abstract class Scanner implements Visitor {

    private Stack<Visitable> stack = new Stack<Visitable>();

    protected final Logger logger = Logger.getLogger(getClass());

    protected Context context;

    public Scanner(Context context) {
        this.context = context;
    }

    @SuppressWarnings("unchecked")
    public Scanner(Scanner parentScanner) {
        this.context = parentScanner.context;
        this.stack = (Stack<Visitable>) parentScanner.stack.clone();
    }

    public void onScanStart() {
    }

    public void onScanEnded() {
    }

    protected String getCurrentContainerName() {
        return getContainerNameAtIndex(0);
    }

    /**
     * Gets the container name at the stack.size()-i level.
     * 
     * @param i
     *            the reversed index (0=top of the stack)
     */
    protected String getContainerNameAtIndex(int i) {
        List<String> modules = new ArrayList<String>();
        for (int j = 0; j < getStack().size() - i; j++) {
            Visitable v = getStack().get(j);
            if (v instanceof ModuleDeclaration) {
                modules.add(((ModuleDeclaration) v).getName());
            }
            if (v instanceof TypeDeclaration && !((TypeDeclaration) v).isAnonymous()) {
                modules.add(((TypeDeclaration) v).getName());
            }
        }
        return StringUtils.join(modules.iterator(), ".");
    }

    protected String getCurrentModuleName() {
        List<String> modules = new ArrayList<String>();
        for (Visitable v : getStack()) {
            if (v instanceof ModuleDeclaration) {
                modules.add(((ModuleDeclaration) v).getName());
            }
        }
        return StringUtils.join(modules.iterator(), ".");
    }

    /**
     * Tells if the given declaration belongs to the current scanning stack.
     */
    protected boolean isInScope(Declaration declaration) {
        boolean inScope = false;
        for (int i = 0; i < getStack().size(); i++) {
            if (getStack().get(i) == declaration) {
                inScope = true;
                break;
            }
        }
        return inScope;
    }

    protected String getCurrentDeclarationName() {
        StringBuffer sb = new StringBuffer();
        for (Visitable v : getStack()) {
            if (v instanceof Declaration) {
                sb.append(((Declaration) v).getName());
                sb.append('.');
            }
        }
        if (!getStack().isEmpty() && !(sb.length() == 0)) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    protected Visitable getRoot() {
        if (getStack().isEmpty()) {
            return null;
        }

        return getStack().get(0);
    }

    protected QualifiedDeclaration<TypeDeclaration> lookupTypeDeclaration(String name) {
        if (name == null) {
            return null;
        }
        // NOTE: it is possible to search using the context find methods... it
        // would be nicer but it may be slower, so we do it this way... to be
        // thought of
        Set<String> possibleNames = new LinkedHashSet<String>();
        String mainModuleName = "";
        if (getRoot() instanceof CompilationUnit) {
            mainModuleName = ((CompilationUnit) getRoot()).getMainModule().getName();
        }

        // lookup in current compilation unit
        for (int i = 0; i < getStack().size(); i++) {
            String containerName = getContainerNameAtIndex(i);
            String declFullName = StringUtils.isBlank(containerName) ? name : containerName + "." + name;
            if (declFullName.startsWith(mainModuleName)) {
                possibleNames.add(declFullName.substring(mainModuleName.length() + 1));
            }
            TypeDeclaration match = context.getTypeDeclaration(declFullName);
            if (match != null) {
                return new QualifiedDeclaration<>(match, declFullName);
            }
        }

        // lookup in all compilation units
        for (CompilationUnit compilUnit : context.compilationUnits) {
            for (String rootRelativeName : possibleNames) {
                TypeDeclaration match = context
                        .getTypeDeclaration(compilUnit.getMainModule().getName() + "." + rootRelativeName);
                if (match != null) {
                    return new QualifiedDeclaration<>(match,
                            compilUnit.getMainModule().getName() + "." + rootRelativeName);
                }
            }
        }

        return null;
    }

    protected QualifiedDeclaration<ModuleDeclaration> lookupModuleDeclaration(String name) {
        Set<String> possibleNames = new LinkedHashSet<String>();
        String mainModuleName = "";
        if (getRoot() instanceof CompilationUnit) {
            mainModuleName = ((CompilationUnit) getRoot()).getMainModule().getName();
        }

        // lookup in current compilation unit
        for (int i = 0; i < getStack().size(); i++) {
            String containerName = getContainerNameAtIndex(i);
            String declFullName = StringUtils.isBlank(containerName) ? name : containerName + "." + name;

            if (declFullName.startsWith(mainModuleName)) {
                possibleNames.add(declFullName.substring(mainModuleName.length() + 1));
            }
            List<QualifiedDeclaration<ModuleDeclaration>> matches = context
                    .findDeclarations(ModuleDeclaration.class, declFullName);
            for (QualifiedDeclaration<ModuleDeclaration> m : matches) {
                return m;
            }
        }

        // lookup in all compilation units
        for (CompilationUnit compilUnit : context.compilationUnits) {
            for (String rootRelativeName : possibleNames) {
                List<QualifiedDeclaration<ModuleDeclaration>> matches = context.findDeclarations(
                        ModuleDeclaration.class, compilUnit.getMainModule().getName() + "." + rootRelativeName);
                for (QualifiedDeclaration<ModuleDeclaration> m : matches) {
                    return m;
                }
            }
        }

        return null;
    }

    protected Type lookupType(TypeReference reference) {
        return lookupType(reference, null, false, false);
    }

    protected Type lookupType(TypeReference reference, String modName) {
        return lookupType(reference, modName, false, false);
    }

    protected boolean isFunctionalTypeReference(TypeReference typeReference) {
        Type t = lookupType(typeReference, null);
        if (t == null || !(t instanceof TypeDeclaration)) {
            return false;
        }
        TypeDeclaration td = (TypeDeclaration) t;
        if (context.getTypeName(td).startsWith(JSweetDefTranslatorConfig.FUNCTION_CLASSES_PACKAGE)
                || context.getTypeName(td).startsWith("java.util.function")) {
            return true;
        }
        if (td.isAnnotationPresent(FunctionalInterface.class)) {
            return true;
        }
        return false;
    }

    protected boolean isSuperTypeReference(TypeReference typeReference) {
        if (!(getParent() instanceof TypeDeclaration)) {
            return false;
        }

        TypeDeclaration parentDeclaration = (TypeDeclaration) getParent();
        if (!ArrayUtils.contains(parentDeclaration.getSuperTypes(), typeReference)) {
            return false;
        }

        return true;
    }

    protected boolean isTypeArgumentTypeReference(TypeReference typeReference) {
        if (!(getParent() instanceof TypeReference)) {
            return false;
        }

        TypeReference parentReference = (TypeReference) getParent();
        if (!ArrayUtils.contains(parentReference.typeArguments, typeReference)) {
            return false;
        }

        return true;
    }

    protected TypeDeclaration extraLookup(TypeReference reference, String modName) {
        if (reference.getName() == null) {
            return null;
        }

        QualifiedDeclaration<TypeDeclaration> type = context.findFirstDeclaration(TypeDeclaration.class,
                reference.getName(), getParent(CompilationUnit.class));
        if (type != null) {
            return type.getDeclaration();
        }
        type = context.findFirstDeclaration(TypeDeclaration.class, "*." + reference.getName(),
                getParent(CompilationUnit.class));
        if (type != null) {
            return type.getDeclaration();
        }
        return null;

        // return lookupInLibModules(context.getLibModule(modName),
        // context.getLibRelativePath(modName), reference);
    }

    protected Type lookupType(TypeReference reference, String modName, boolean createIfNotFound,
            boolean verboseIfNotFound) {
        if (reference.getDeclaration() != null) {
            return reference.getDeclaration();
        }
        if (reference.isTypeOf()) {
            return null;
        }
        if (reference.isObjectType()) {
            reference.setDeclaration(reference.getObjectType());
            return reference.getObjectType();
        } else {
            // lookup in the global type repository
            if (modName == null) {
                modName = getCurrentModuleName();
            }
            TypeDeclaration t = context.getTypeDeclaration(modName + "." + reference.getName());
            if (t == null) {
                t = context.getTypeDeclaration(reference.getName());
                if (t == null) {
                    String containerName = getCurrentContainerName();
                    t = context.getTypeDeclaration(containerName + "." + reference.getName());
                    if (t == null) {
                        if (!JSweetDefTranslatorConfig.isJDKReplacementMode()) {
                            t = context.getTypeDeclaration("java.lang." + reference.getName());
                        }
                        if (t == null) {
                            t = context.getTypeDeclaration(
                                    JSweetDefTranslatorConfig.GLOBALS_PACKAGE_NAME + "." + reference.getName());
                            if (t == null) {
                                t = context.getTypeDeclaration(
                                        JSweetDefTranslatorConfig.LANG_PACKAGE + "." + reference.getName());
                                if (t == null) {
                                    t = context.getTypeDeclaration(
                                            JSweetDefTranslatorConfig.DOM_PACKAGE + "." + reference.getName());
                                    if (t == null) {
                                        String[] subNames = modName.split("\\.");
                                        String partialModName;
                                        for (int i = subNames.length - 1; i > 0; i--) {
                                            String[] a = ArrayUtils.subarray(subNames, 0, i);
                                            partialModName = StringUtils.join(a, ".");
                                            t = context
                                                    .getTypeDeclaration(partialModName + "." + reference.getName());
                                            if (t != null) {
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // lookup in type parameters
            if (t == null) {
                TypeParameterizedElement tpe = getParent(TypeParameterizedElement.class, true);
                while (tpe != null) {
                    if (tpe.getTypeParameters() != null) {
                        for (TypeParameterDeclaration d : tpe.getTypeParameters()) {
                            if (d.getName() != null && d.getName().equals(reference.getName())) {
                                reference.setDeclaration(d);
                                return d;
                            }
                        }
                    }
                    tpe = getParent(TypeParameterizedElement.class, tpe);
                }
            }

            if (t == null) {
                t = extraLookup(reference, modName);
            }

            if (t == null) {
                QualifiedDeclaration<TypeDeclaration> match = lookupTypeDeclaration(reference.getName());
                if (match != null) {
                    t = match.getDeclaration();
                }
            }

            if (t == null) {
                if (createIfNotFound) {
                    Token token = getCurrentToken();
                    System.err.println("WARNING: creating unknown reference " + reference + " at "
                            + (token == null ? "" : token.getLocation()));
                    String[] names = reference.getName().split("\\.");
                    TypeDeclaration type = new TypeDeclaration(null, "class", names[names.length - 1], null, null,
                            null);
                    ModuleDeclaration module;
                    if (names.length > 1) {
                        module = new ModuleDeclaration(null,
                                StringUtils.join(ArrayUtils.subarray(names, 0, names.length - 1), "."),
                                new Declaration[] { type });
                        context.registerModule(module.getName(), module);
                    } else {
                        module = new ModuleDeclaration(null, JSweetDefTranslatorConfig.GLOBALS_PACKAGE_NAME,
                                new Declaration[] { type });
                    }
                    context.compilationUnits.get(0).addMember(module);
                    context.registerType(module.getName() + "." + type.getName(), type);
                } else {
                    if (verboseIfNotFound) {
                        Token token = getCurrentToken();
                        context.reportError("cannot find reference " + reference + " (" + getCurrentContainerName()
                                + "." + reference.getName() + ")", token);
                    }
                }
            }

            reference.setDeclaration(t);
            return t;
        }
    }

    protected QualifiedDeclaration<FunctionDeclaration> lookupFunctionDeclaration(String name) {
        Set<String> possibleNames = new LinkedHashSet<String>();
        String mainModuleName = "";
        if (getRoot() instanceof CompilationUnit) {
            ModuleDeclaration mainModule = ((CompilationUnit) getRoot()).getMainModule();
            if (mainModule != null) {
                mainModuleName = mainModule.getName();
            }
        }

        // lookup in current compilation unit
        for (int i = 0; i < getStack().size(); i++) {
            String containerName = getContainerNameAtIndex(i);
            String declFullName = StringUtils.isBlank(containerName) ? name : containerName + "." + name;

            if (declFullName.startsWith(mainModuleName)) {
                possibleNames.add(declFullName.substring(mainModuleName.length() + 1));
            }
            List<QualifiedDeclaration<FunctionDeclaration>> matches = context
                    .findDeclarations(FunctionDeclaration.class, declFullName);
            if (matches.size() > 0) {
                return matches.get(0);
            }
        }

        // lookup in all compilation units
        for (CompilationUnit compilUnit : context.compilationUnits) {
            if (compilUnit.getMainModule() == null) {
                continue;
            }
            for (String rootRelativeName : possibleNames) {
                List<QualifiedDeclaration<FunctionDeclaration>> matches = context.findDeclarations(
                        FunctionDeclaration.class, compilUnit.getMainModule().getName() + "." + rootRelativeName);
                if (matches.size() > 0) {
                    return matches.get(0);
                }
            }
        }

        return null;
    }

    protected QualifiedDeclaration<VariableDeclaration> lookupVariableDeclaration(String name) {
        Set<String> possibleNames = new LinkedHashSet<String>();
        String mainModuleName = "";
        if (getRoot() instanceof CompilationUnit) {
            ModuleDeclaration mainModule = ((CompilationUnit) getRoot()).getMainModule();
            if (mainModule != null) {
                mainModuleName = mainModule.getName();
            }
        }

        // lookup in current compilation unit
        for (int i = 0; i < getStack().size(); i++) {
            String containerName = getContainerNameAtIndex(i);
            String declFullName = StringUtils.isBlank(containerName) ? name : containerName + "." + name;

            if (declFullName.startsWith(mainModuleName)) {
                possibleNames.add(declFullName.substring(mainModuleName.length() + 1));
            }
            List<QualifiedDeclaration<VariableDeclaration>> matches = context
                    .findDeclarations(VariableDeclaration.class, declFullName);
            if (matches.size() > 0) {
                return matches.get(0);
            }
        }

        // lookup in all compilation units
        for (CompilationUnit compilUnit : context.compilationUnits) {
            if (compilUnit.getMainModule() == null) {
                continue;
            }
            for (String rootRelativeName : possibleNames) {
                List<QualifiedDeclaration<VariableDeclaration>> matches = context.findDeclarations(
                        VariableDeclaration.class, compilUnit.getMainModule().getName() + "." + rootRelativeName);
                if (matches.size() > 0) {
                    return matches.get(0);
                }
            }
        }

        return null;
    }

    protected FunctionDeclaration lookupFunctionDeclaration(TypeReference typeReference, String name,
            TypeReference... argTypes) {
        TypeDeclaration type = (TypeDeclaration) lookupType(typeReference, null);
        if (type == null) {
            return null;
        }
        boolean found = false;
        for (Declaration d : type.getMembers()) {
            if (name.equals(d.getName())) {
                if (d instanceof FunctionDeclaration) {
                    FunctionDeclaration function = (FunctionDeclaration) d;
                    if (argTypes.length == function.getParameters().length) {
                        found = true;
                        for (int i = 0; i < argTypes.length; i++) {
                            if (!argTypes[i].equals(function.getParameters()[i].getType())) {
                                found = false;
                            }
                        }
                        if (found) {
                            return function;
                        }
                    }
                }
            }
        }
        if (type.getSuperTypes() != null) {
            for (TypeReference t : type.getSuperTypes()) {
                FunctionDeclaration f = lookupFunctionDeclaration(t, name, argTypes);
                if (f != null) {
                    return f;
                }
            }
        }
        return null;
    }

    public void printStackTrace(PrintStream out) {
        out.println("Dumping scanner stack: " + this.getClass().getSimpleName() + " - " + stack.size());
        for (int i = stack.size() - 1; i >= 0; i--) {
            if (stack.get(i) instanceof AstNode) {
                AstNode node = (AstNode) stack.get(i);
                out.println("   " + node.getClass().getSimpleName() + " - " + (node.getToken() == null ? "N/A"
                        : node.getToken() + " " + node.getToken().getLocation()));
            }
        }
    }

    public Token getCurrentToken() {
        for (int i = stack.size() - 1; i >= 0; i--) {
            if (stack.get(i) instanceof AstNode) {
                AstNode node = (AstNode) stack.get(i);
                if (node.getToken() != null) {
                    return node.getToken();
                }
            }
        }
        return null;
    }

    protected void enter(Visitable element) {
        if (!stack.isEmpty() && stack.peek() == element) {
            printStackTrace(System.err);
            logger.error("FATAL ERROR: duplicate entry: " + element);
            throw new RuntimeException("FATAL ERROR: duplicate entry: " + element);
        }
        stack.push(element);
    }

    protected void exit() {
        stack.pop();
    }

    public Stack<Visitable> getStack() {
        return this.stack;
    }

    /**
     * Gets the current parent AST node from the stack.
     */
    public Visitable getParent() {
        return this.stack.get(this.stack.size() - 2);
    }

    /**
     * Gets the nth level parent AST node from the stack (getParent(1) ==
     * getParent()).
     */
    public Visitable getParent(int level) {
        return this.stack.get(this.stack.size() - (level + 1));
    }

    public <T extends Visitable> T getParent(Predicate<Visitable> predicate) {
        return getParent(predicate, false);
    }

    public <T extends Visitable> T getParent(Class<T> type) {
        return getParent(type, false);
    }

    @SuppressWarnings("unchecked")
    public <T extends Visitable> T getParent(Class<T> type, boolean includeCurrent) {
        for (int i = this.stack.size() - (includeCurrent ? 1 : 2); i >= 0; i--) {
            if (type.isAssignableFrom(this.stack.get(i).getClass())) {
                return (T) this.stack.get(i);
            }
        }
        return null;
    }

    public <T extends Visitable> List<T> getParents(Class<T> type) {
        List<T> parents = new ArrayList<T>();
        for (int i = this.stack.size() - 1; i >= 0; i--) {
            if (type.isAssignableFrom(this.stack.get(i).getClass())) {
                @SuppressWarnings("unchecked")
                T t = (T) this.stack.get(i);
                parents.add(t);
            }
        }
        return parents;
    }

    @SuppressWarnings("unchecked")
    public <T extends Visitable> List<T> getParents(Predicate<Visitable> predicate) {
        List<T> parents = new ArrayList<T>();
        for (int i = this.stack.size() - 1; i >= 0; i--) {
            if (predicate.test(this.stack.get(i))) {
                T t = (T) this.stack.get(i);
                parents.add(t);
            }
        }
        return parents;
    }

    @SuppressWarnings("unchecked")
    public <T extends Visitable> T getParent(Predicate<Visitable> predicate, boolean includeCurrent) {
        for (int i = this.stack.size() - (includeCurrent ? 1 : 2); i >= 0; i--) {
            if (predicate.test(this.stack.get(i))) {
                return (T) this.stack.get(i);
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public <T extends Visitable> T getParent(Class<T> type, Visitable from) {
        for (int i = this.stack.size() - 1; i >= 0; i--) {
            if (this.stack.get(i) == from) {
                for (int j = i - 1; j >= 0; j--) {
                    if (type.isAssignableFrom(this.stack.get(j).getClass())) {
                        return (T) this.stack.get(j);
                    }
                }
                return null;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public <T extends Visitable> T getParent(Predicate<Visitable> predicate, Visitable from) {
        for (int i = this.stack.size() - 1; i >= 0; i--) {
            if (this.stack.get(i) == from) {
                for (int j = i - 1; j >= 0; j--) {
                    if (predicate.test(this.stack.get(j))) {
                        return (T) this.stack.get(j);
                    }
                }
                return null;
            }
        }
        return null;
    }

    public void scan(Visitable visitable) {
        if (visitable != null && !visitable.isHidden()) {
            enter(visitable);
            try {
                visitable.accept(this);
            } finally {
                exit();
            }
        }
    }

    public void scan(Visitable[] visitables) {
        if (visitables != null) {
            for (Visitable visitable : visitables) {
                scan(visitable);
            }
        }
    }

    public void scan(List<? extends Visitable> visitables) {
        if (visitables != null) {
            for (Visitable visitable : visitables) {
                scan(visitable);
            }
        }
    }

    @Override
    public void visitCompilationUnit(CompilationUnit compilationUnit) {
        scan(compilationUnit.getDeclarations());
    }

    @Override
    public void visitModuleDeclaration(ModuleDeclaration moduleDeclaration) {
        scan(moduleDeclaration.getMembers());
    }

    @Override
    public void visitTypeDeclaration(TypeDeclaration typeDeclaration) {
        scan(typeDeclaration.getTypeParameters());
        scan(typeDeclaration.getSuperTypes());
        scan(typeDeclaration.getMergedSuperTypes());
        scan(typeDeclaration.getMembers());
    }

    @Override
    public void visitFunctionDeclaration(FunctionDeclaration functionDeclaration) {
        scan(functionDeclaration.getTypeParameters());
        scan(functionDeclaration.getType());
        scan(functionDeclaration.getParameters());
    }

    @Override
    public void visitVariableDeclaration(VariableDeclaration variableDeclaration) {
        scan(variableDeclaration.getType());
        scan(variableDeclaration.getInitializer());
    }

    @Override
    public void visitParameterDeclaration(ParameterDeclaration parameterDeclaration) {
        scan(parameterDeclaration.getType());
    }

    @Override
    public void visitTypeReference(TypeReference typeReference) {
        scan(typeReference.getObjectType());
        scan(typeReference.getTypeArguments());
    }

    @Override
    public void visitTypeMacro(TypeMacroDeclaration typeMacroDeclaration) {
        scan(typeMacroDeclaration.getTypeParameters());
        scan(typeMacroDeclaration.getType());
    }

    @Override
    public void visitFunctionalTypeReference(FunctionalTypeReference functionalTypeReference) {
        scan(functionalTypeReference.getReturnType());
        scan(functionalTypeReference.getParameters());
    }

    @Override
    public void visitArrayTypeReference(ArrayTypeReference arrayTypeReference) {
        scan(arrayTypeReference.getComponentType());
    }

    @Override
    public void visitUnionTypeReference(UnionTypeReference unionTypeReference) {
        switch (unionTypeReference.getSelected()) {
        case LEFT:
            scan(unionTypeReference.getLeftType());
            break;
        case RIGHT:
            scan(unionTypeReference.getRightType());
            break;
        default:
            scan(unionTypeReference.getLeftType());
            scan(unionTypeReference.getRightType());
        }
    }

    @Override
    public void visitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) {
        scan(typeParameterDeclaration.getUpperBound());
    }

    @Override
    public void visitLiteral(Literal literal) {
    }

    @Override
    public void visitReferenceDeclaration(ReferenceDeclaration referenceDeclaration) {
    }

    protected Pair<TypeDeclaration, FunctionDeclaration> findSuperMethod(TypeDeclaration declaringType,
            FunctionDeclaration method) {
        MutablePair<TypeDeclaration, FunctionDeclaration> superMethodInfos = new MutablePair<>();
        applyToSuperMethod(declaringType, method, (superType, superMethod) -> {
            superMethodInfos.setLeft(superType);
            superMethodInfos.setRight(superMethod);
        });

        return superMethodInfos.getRight() == null ? null : superMethodInfos;
    }

    protected void applyToSuperMethod(TypeDeclaration declaringType, FunctionDeclaration childFunction,
            BiConsumer<TypeDeclaration, FunctionDeclaration> apply) {
        applyToSuperMethod(declaringType, childFunction, declaringType, apply);
    }

    private void applyToSuperMethod(TypeDeclaration declaringType, FunctionDeclaration childFunction,
            TypeDeclaration parentType, BiConsumer<TypeDeclaration, FunctionDeclaration> apply) {
        int index = -1;
        if (declaringType != parentType) {
            index = ArrayUtils.indexOf(parentType.getMembers(), childFunction);
        }
        if (index != -1) {
            apply.accept(parentType, (FunctionDeclaration) parentType.getMembers()[index]);
        } else {
            if (parentType.getSuperTypes() != null && parentType.getSuperTypes().length > 0) {
                for (TypeReference ref : parentType.getSuperTypes()) {
                    Type decl = lookupType(ref, null);
                    if (decl instanceof TypeDeclaration) {
                        applyToSuperMethod(declaringType, childFunction, (TypeDeclaration) decl, apply);
                    }
                }
            } else if (!JSweetDefTranslatorConfig.getObjectClassName().equals(context.getTypeName(parentType))) {
                TypeDeclaration decl = context.getTypeDeclaration(JSweetDefTranslatorConfig.getObjectClassName());
                if (decl != null) {
                    applyToSuperMethod(declaringType, childFunction, (TypeDeclaration) decl, apply);
                }
            }
        }
    }
}