com.perl5.lang.perl.util.PerlSubUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.perl5.lang.perl.util.PerlSubUtil.java

Source

/*
 * Copyright 2015 Alexandr Evstigneev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.perl5.lang.perl.util;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import com.perl5.compat.PerlStubIndex;
import com.perl5.lang.perl.PerlScopes;
import com.perl5.lang.perl.extensions.packageprocessor.PerlExportDescriptor;
import com.perl5.lang.perl.lexer.PerlElementTypes;
import com.perl5.lang.perl.psi.*;
import com.perl5.lang.perl.psi.mro.PerlMro;
import com.perl5.lang.perl.psi.references.PerlSubReference;
import com.perl5.lang.perl.psi.stubs.subsdeclarations.PerlSubDeclarationStubIndex;
import com.perl5.lang.perl.psi.stubs.subsdefinitions.PerlSubDefinitionsStubIndex;
import com.perl5.lang.perl.psi.utils.PerlSubArgument;
import com.perl5.lang.perl.util.processors.PerlImportsCollector;
import com.perl5.lang.perl.util.processors.PerlSubImportsCollector;
import gnu.trove.THashSet;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;

/**
 * Created by hurricup on 19.04.2015.
 */
public class PerlSubUtil implements PerlElementTypes, PerlBuiltInSubs {
    public static final String SUB_AUTOLOAD = "AUTOLOAD";
    public static final String SUB_AUTOLOAD_WITH_PREFIX = PerlPackageUtil.PACKAGE_SEPARATOR + SUB_AUTOLOAD;

    /**
     * Checks if provided function is built in
     *
     * @param function function name
     * @return checking result
     */
    public static boolean isBuiltIn(String function) {
        return BUILT_IN.contains(function);
    }

    /**
     * Checks if sub defined as unary with ($) proto
     *
     * @param packageName package name
     * @param subName     sub name
     * @return check result
     */
    public static boolean isUnary(@Nullable String packageName, @NotNull String subName) {
        // todo implement checking
        return false;
    }

    /**
     * Checks if sub defined as unary with () proto
     *
     * @param packageName package name
     * @param subName     sub name
     * @return check result
     */
    public static boolean isArgumentless(@Nullable String packageName, @NotNull String subName) {
        // todo implement checking
        return false;
    }

    /**
     * Searching project files for sub definitions by specific package and function name
     *
     * @param project       project to search in
     * @param canonicalName canonical function name package::name
     * @return Collection of found definitions
     */
    public static Collection<PerlSubDefinitionBase> getSubDefinitions(Project project, String canonicalName) {
        return getSubDefinitions(project, canonicalName, PerlScopes.getProjectAndLibrariesScope(project));
    }

    public static Collection<PerlSubDefinitionBase> getSubDefinitions(Project project, String canonicalName,
            GlobalSearchScope scope) {
        if (canonicalName == null) {
            return Collections.emptyList();
        }
        return PerlStubIndex.getElements(PerlSubDefinitionsStubIndex.KEY, canonicalName, project, scope,
                PerlSubDefinitionBase.class);
    }

    public static boolean isSubDefinitionsIndexAvailable() {
        return PerlStubIndex.getInstance().isIndexAvailable(PerlSubDefinitionsStubIndex.KEY);
    }

    /**
     * Returns list of defined subs names
     *
     * @param project project to search in
     * @return collection of sub names
     */
    public static Collection<String> getDefinedSubsNames(Project project) {
        return PerlUtil.getIndexKeysWithoutInternals(PerlSubDefinitionsStubIndex.KEY, project);
    }

    /**
     * Processes all defined subs names with given processor
     *
     * @param project   project to search in
     * @param processor string processor for suitable strings
     * @return collection of constants names
     */
    public static boolean processDefinedSubsNames(Project project, Processor<String> processor) {
        return PerlStubIndex.getInstance().processAllKeys(PerlSubDefinitionsStubIndex.KEY, project, processor);
    }

    /**
     * Searching project files for sub declarations by specific package and function name
     *
     * @param project       project to search in
     * @param canonicalName canonical function name package::name
     * @return Collection of found definitions
     */
    public static Collection<PerlSubDeclaration> getSubDeclarations(Project project, String canonicalName) {
        return getSubDeclarations(project, canonicalName, PerlScopes.getProjectAndLibrariesScope(project));
    }

    public static Collection<PerlSubDeclaration> getSubDeclarations(Project project, String canonicalName,
            GlobalSearchScope scope) {
        if (canonicalName == null) {
            return Collections.emptyList();
        }
        return PerlStubIndex.getElements(PerlSubDeclarationStubIndex.KEY, canonicalName, project, scope,
                PerlSubDeclaration.class);
    }

    /**
     * Returns list of declared subs names
     *
     * @param project project to search in
     * @return collection of sub names
     */
    public static Collection<String> getDeclaredSubsNames(Project project) {
        return PerlUtil.getIndexKeysWithoutInternals(PerlSubDeclarationStubIndex.KEY, project);
    }

    public static boolean isSubDeclarationsIndexAvailable() {
        return PerlStubIndex.getInstance().isIndexAvailable(PerlSubDeclarationStubIndex.KEY);
    }

    /**
     * Processes all declared subs names with given processor
     *
     * @param project   project to search in
     * @param processor string processor for suitable strings
     * @return collection of constants names
     */
    public static boolean processDeclaredSubsNames(Project project, Processor<String> processor) {
        return PerlStubIndex.getInstance().processAllKeys(PerlSubDeclarationStubIndex.KEY, project, processor);
    }

    /**
     * Detects return value of method container
     *
     * @param methodContainer method container inspected
     * @return package name or null
     */
    @Nullable
    public static String getMethodReturnValue(PerlMethodContainer methodContainer) {
        if (methodContainer.getMethod() != null && methodContainer.getMethod().getSubNameElement() != null) {
            // fixme this should be moved to a method
            PerlMethod methodElement = methodContainer.getMethod();
            PerlSubNameElement subNameElement = methodElement.getSubNameElement();

            if ("new".equals(subNameElement.getName())) {
                return methodElement.getPackageName();
            }

            PsiReference reference = subNameElement.getReference();

            if (reference instanceof PerlSubReference) {
                for (ResolveResult resolveResult : ((PerlSubReference) reference).multiResolve(false)) {
                    PsiElement targetElement = resolveResult.getElement();
                    if (targetElement instanceof PerlSubDefinitionBase
                            && ((PerlSubDefinitionBase) targetElement).getReturns() != null) {
                        return ((PerlSubDefinitionBase) targetElement).getReturns();
                    } else if (targetElement instanceof PerlSubDeclaration
                            && ((PerlSubDeclaration) targetElement).getReturns() != null) {
                        return ((PerlSubDeclaration) targetElement).getReturns();
                    }
                }
            }
        }

        return null;
    }

    /**
     * Returns a list of imported descriptors
     *
     * @param rootElement element to start looking from
     * @return result map
     */
    @NotNull
    public static List<PerlExportDescriptor> getImportedSubsDescriptors(@NotNull PsiElement rootElement) {
        PerlImportsCollector collector = new PerlSubImportsCollector();
        PerlUtil.processImportedEntities(rootElement, collector);
        return collector.getResult();
    }

    /**
     * Builds arguments string for presentation
     *
     * @param subArguments list of arguments
     * @return stringified prototype
     */
    public static String getArgumentsListAsString(List<PerlSubArgument> subArguments) {
        int argumentsNumber = subArguments.size();

        List<String> argumentsList = new ArrayList<String>();
        List<String> optionalAargumentsList = new ArrayList<String>();

        for (PerlSubArgument argument : subArguments) {
            if (!optionalAargumentsList.isEmpty() || argument.isOptional()) {
                optionalAargumentsList.add(argument.toStringShort());
            } else {
                argumentsList.add(argument.toStringShort());
            }

            int compiledListSize = argumentsList.size() + optionalAargumentsList.size();
            if (compiledListSize > 5 && argumentsNumber > compiledListSize) {
                if (!optionalAargumentsList.isEmpty()) {
                    optionalAargumentsList.add("...");
                } else {
                    argumentsList.add("...");
                }
                break;
            }
        }

        if (argumentsList.isEmpty() && optionalAargumentsList.isEmpty()) {
            return "";
        }

        String argumentListString = StringUtils.join(argumentsList, ", ");
        String optionalArgumentsString = StringUtils.join(optionalAargumentsList, ", ");

        if (argumentListString.isEmpty()) {
            return "([" + optionalArgumentsString + "])";
        }
        if (optionalAargumentsList.isEmpty()) {
            return "(" + argumentListString + ")";
        } else {
            return "(" + argumentListString + " [, " + optionalArgumentsString + "])";
        }
    }

    @NotNull
    public static PerlSubBase getTopLevelSuperMethod(@NotNull PerlSubBase subBase) {
        return getTopLevelSuperMethod(subBase, new THashSet<String>());
    }

    /**
     * Recursively collecting superMethods
     *
     * @param subBase        base method to search
     * @param classRecursion class recursion set
     * @return empty list if we've already been in this class, or list of topmost methods
     */
    @NotNull
    private static PerlSubBase getTopLevelSuperMethod(@NotNull PerlSubBase subBase, Set<String> classRecursion) {
        String packageName = subBase.getPackageName();

        if (packageName == null || classRecursion.contains(packageName)) {
            return subBase;
        }

        classRecursion.add(packageName);

        PerlSubBase directSuperMethod = getDirectSuperMethod(subBase);
        return directSuperMethod == null ? subBase : getTopLevelSuperMethod(directSuperMethod, classRecursion);
    }

    @Nullable
    public static PerlSubBase getDirectSuperMethod(PerlSubBase subBase) {
        if (!subBase.isMethod()) {
            return null;
        }

        Collection<PsiElement> resolveTargets = PerlMro.resolveSub(subBase.getProject(), subBase.getPackageName(),
                subBase.getSubName(), true);

        for (PsiElement resolveTarget : resolveTargets) {
            if (resolveTarget instanceof PerlSubBase) {
                return (PerlSubBase) resolveTarget;
            }
        }
        return null;
    }

    @NotNull
    public static List<PerlSubBase> collectOverridingSubs(PerlSubBase subBase) {
        return collectOverridingSubs(subBase, new THashSet<String>());
    }

    @NotNull
    public static List<PerlSubBase> collectOverridingSubs(@NotNull PerlSubBase subBase,
            @NotNull Set<String> recursionSet) {
        List<PerlSubBase> result = new ArrayList<PerlSubBase>();
        for (PerlSubBase directDescendant : getDirectOverridingSubs(subBase)) {
            String packageName = directDescendant.getPackageName();
            if (StringUtil.isNotEmpty(packageName) && !recursionSet.contains(packageName)) {
                recursionSet.add(packageName);
                result.add(directDescendant);
                result.addAll(collectOverridingSubs(directDescendant, recursionSet));
            }
        }

        return result;
    }

    @NotNull
    public static List<PerlSubBase> getDirectOverridingSubs(@NotNull PerlSubBase subBase) {
        PerlNamespaceDefinition containingNamespace = PerlPackageUtil.getContainingNamespace(subBase);

        return containingNamespace == null ? Collections.<PerlSubBase>emptyList()
                : getDirectOverridingSubs(subBase, containingNamespace);
    }

    @NotNull
    public static List<PerlSubBase> getDirectOverridingSubs(@NotNull final PerlSubBase subBase,
            @NotNull PerlNamespaceDefinition containingNamespace) {
        final List<PerlSubBase> overridingSubs = new ArrayList<PerlSubBase>();
        final String subName = subBase.getSubName();

        PerlPackageUtil.processChildNamespacesSubs(containingNamespace, null, new Processor<PerlSubBase>() {
            @Override
            public boolean process(PerlSubBase overridingSub) {
                String overridingSubName = overridingSub.getSubName();
                if (StringUtil.equals(overridingSubName, subName)
                        && subBase == getDirectSuperMethod(overridingSub)) {
                    overridingSubs.add(overridingSub);
                    return false;
                }
                return true;
            }
        });

        return overridingSubs;
    }

    @NotNull
    public static List<PsiElement> collectRelatedItems(@NotNull String canonicalName, @NotNull Project project) {
        final List<PsiElement> result = new ArrayList<PsiElement>();
        processRelatedItems(canonicalName, project, new Processor<PsiElement>() {
            @Override
            public boolean process(PsiElement element) {
                result.add(element);
                return true;
            }
        });
        return result;
    }

    public static void processRelatedItems(@NotNull String canonicalName, @NotNull Project project,
            @NotNull Processor<PsiElement> processor) {
        processRelatedItems(canonicalName, project, PerlScopes.getProjectAndLibrariesScope(project), processor);
    }

    // fixme this should replace PerlSubReferenceResolver#collectRelatedItems
    public static void processRelatedItems(@NotNull String canonicalName, @NotNull Project project,
            @NotNull GlobalSearchScope searchScope, @NotNull Processor<PsiElement> processor) {
        for (PerlSubDefinitionBase target : PerlSubUtil.getSubDefinitions(project, canonicalName, searchScope)) {
            if (!processor.process(target)) {
                return;
            }
        }
        for (PerlSubDeclaration target : PerlSubUtil.getSubDeclarations(project, canonicalName, searchScope)) {
            if (!processor.process(target)) {
                return;
            }
        }
        for (PerlGlobVariable target : PerlGlobUtil.getGlobsDefinitions(project, canonicalName, searchScope)) {
            if (!processor.process(target)) {
                return;
            }
        }
    }
}