You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Eric Milles (Jira)" <ji...@apache.org> on 2021/06/04 15:39:00 UTC

[jira] [Comment Edited] (GROOVY-10123) Consolidate methods for collecting interfaces

    [ https://issues.apache.org/jira/browse/GROOVY-10123?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17357441#comment-17357441 ] 

Eric Milles edited comment on GROOVY-10123 at 6/4/21, 3:38 PM:
---------------------------------------------------------------

This came up while investigating GROOVY-8638.  I was looking for a way to get all methods for a type and then group by overrides.  This is not easy to sort out given the current APIs.

This is what I came up with to find the non-synthetic methods of a given type:
{code:java}
    protected Collection<MethodNode> getAllMethods(ClassNode type, Set<ClassNode> exclude) {
        Map<String, MethodNode> methods = new HashMap<>();

        BiConsumer<MethodNode, Map<String, ClassNode>> mapper = (mn, spec) -> {
            StringBuilder sb = new StringBuilder(mn.getName());
            sb.append('(');
            for (org.codehaus.groovy.ast.Parameter p : mn.getParameters()) {
                ClassNode pt = p.getOriginType();
                if (!pt.isGenericsPlaceHolder()) {
                    sb.append(pt.getName());
                } else {
                    sb.append(spec.getOrDefault(pt.getUnresolvedName(), pt).getName());
                }
                sb.append(';');
            }

            methods.merge(sb.toString(), mn, (m1, m2) ->
                // keep override method unless it's synthetic
                !Flags.isSynthetic(m1.getModifiers()) ? m1 : m2);
        };

        Set<ClassNode> types = new LinkedHashSet<>();
        VariableScope.createTypeHierarchy(type, types, true); // GRECLIPSE API; you would need to use a GROOVY API here
        for (ClassNode cn : types) {
            if ((exclude != null && exclude.contains(cn)) ||
                Flags.isSynthetic(cn.getModifiers())) continue;

            Map<String, ClassNode> spec = GenericsUtils.createGenericsSpec(cn);
            for (MethodNode mn : cn.getMethods()) {
                mapper.accept(mn, spec);
            }
        }

        if (exclude != null) exclude.addAll(types); // exclude types next time

        return methods.values();
    }
{code}

I think {{ClassNode#getDeclaredMethodsMap}} could be overloaded to accept a key function and merge strategy.  Or something like that could be offered by {{ClassNodeUtils}}.  I just never got around to making something publicly available out of this.


was (Author: emilles):
This came up while investigating GROOVY-8638.  I was looking for a way to get all methods for a type and then group by overrides.  This is not easy to sort out given the current APIs.

This is what I came up with to find the non-synthetic methods of a given type:
{code:java}
    protected Collection<MethodNode> getAllMethods(ClassNode type, Set<ClassNode> exclude) {
        Map<String, MethodNode> methods = new HashMap<>();

        BiConsumer<MethodNode, Map<String, ClassNode>> mapper = (mn, spec) -> {
            StringBuilder sb = new StringBuilder(mn.getName());
            sb.append('(');
            for (org.codehaus.groovy.ast.Parameter p : mn.getParameters()) {
                ClassNode pt = p.getOriginType();
                if (!pt.isGenericsPlaceHolder()) {
                    sb.append(pt.getName());
                } else {
                    sb.append(spec.getOrDefault(pt.getUnresolvedName(), pt).getName());
                }
                sb.append(';');
            }

            methods.merge(sb.toString(), mn, (m1, m2) ->
                // keep override method unless it's synthetic
                !Flags.isSynthetic(m1.getModifiers()) ? m1 : m2);
        };

        Set<ClassNode> types = new LinkedHashSet<>();
        VariableScope.createTypeHierarchy(type, types, true); // GRECLIPSE API; you would need to use a GROOVY API here
        for (ClassNode cn : types) {
            if ((exclude != null && exclude.contains(cn)) ||
                Flags.isSynthetic(cn.getModifiers())) continue;

            Map<String, ClassNode> spec = GenericsUtils.createGenericsSpec(cn);
            for (MethodNode mn : cn.getMethods()) {
                mapper.accept(mn, spec);
            }
        }

        if (exclude != null) exclude.addAll(types); // exclude types next time

        return methods.values();
    }
{code}

> Consolidate methods for collecting interfaces
> ---------------------------------------------
>
>                 Key: GROOVY-10123
>                 URL: https://issues.apache.org/jira/browse/GROOVY-10123
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Eric Milles
>            Assignee: Eric Milles
>            Priority: Minor
>              Labels: breaking
>
> There are a number of methods presented for collecting interfaces, each with some subtle difference.
> {code:java}
> // interface collection
> org.codehaus.groovy.ast.tools.GeneralUtils#getInterfacesAndSuperInterfaces(ClassNode) // if parameter is interface, returns singleton set
> org.codehaus.groovy.ast.ClassNode#getAllInterfaces() // includes this, excludes super types
> org.codehaus.groovy.transform.trait.Traits.collectAllInterfacesReverseOrder(ClassNode, LinkedHashSet<ClassNode>) // parameterizes results
> org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.collectAllInterfaces(ClassNode) // unused by current codebase; redundant with GeneralUtils#getInterfacesAndSuperInterfaces
> // method collection
> org.codehaus.groovy.ast.ClassNode#getAllDeclaredMethods() // deduplicated by MethodNode#getTypeDescriptor() which keeps bridge/synthetics instead of overridden
> org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor.collectAllInterfaceMethodsByName(ClassNode, String, List<MethodNode>) // non-static and available only to subclasses
> org.apache.groovy.ast.tools.ClassNodeUtils.addDeclaredMethodsFromInterfaces(ClassNode, Map<String, MethodNode>) // non-synthetic already
> org.apache.groovy.ast.tools.ClassNodeUtils.addDeclaredMethodsFromAllInterfaces(ClassNode, Map<String, MethodNode>) // includes synthetics, not all interfaces
> {code}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)