test/jdk/tools/jpackage/helpers/jdk/jpackage/test/MethodCall.java
author herrick
Wed, 16 Oct 2019 10:32:08 -0400
branchJDK-8200758-branch
changeset 58648 3bf53ffa9ae7
parent 58416 f09bf58c1f17
permissions -rw-r--r--
8232279 : Improve test helpers #2 Submitted-by: asemenyuk Reviewed-by: aherrick, almatvee

/*
 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package jdk.jpackage.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jdk.jpackage.test.Functional.ThrowingConsumer;
import jdk.jpackage.test.TestInstance.TestDesc;

class MethodCall implements ThrowingConsumer {

    MethodCall(Object[] instanceCtorArgs, Method method) {
        this.ctorArgs = Optional.ofNullable(instanceCtorArgs).orElse(
                DEFAULT_CTOR_ARGS);
        this.method = method;
        this.methodArgs = new Object[0];
    }

    MethodCall(Object[] instanceCtorArgs, Method method, Object arg) {
        this.ctorArgs = Optional.ofNullable(instanceCtorArgs).orElse(
                DEFAULT_CTOR_ARGS);
        this.method = method;
        this.methodArgs = new Object[]{arg};
    }

    TestDesc createDescription() {
        var descBuilder = TestDesc.createBuilder().method(method);
        if (methodArgs.length != 0) {
            descBuilder.methodArgs(methodArgs);
        }

        if (ctorArgs.length != 0) {
            descBuilder.ctorArgs(ctorArgs);
        }

        return descBuilder.get();
    }

    Method getMethod() {
        return method;
    }

    Object newInstance() throws NoSuchMethodException, InstantiationException,
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        if ((method.getModifiers() & Modifier.STATIC) != 0) {
            return null;
        }

        Constructor ctor = findRequiredConstructor(method.getDeclaringClass(),
                ctorArgs);
        if (ctor.isVarArgs()) {
            // Assume constructor doesn't have fixed, only variable parameters.
            return ctor.newInstance(new Object[]{ctorArgs});
        }

        return ctor.newInstance(ctorArgs);
    }

    void checkRequiredConstructor() throws NoSuchMethodException {
        if ((method.getModifiers() & Modifier.STATIC) == 0) {
            findRequiredConstructor(method.getDeclaringClass(), ctorArgs);
        }
    }

    private static Constructor findVarArgConstructor(Class type) {
        return Stream.of(type.getConstructors()).filter(
                Constructor::isVarArgs).findFirst().orElse(null);
    }

    private Constructor findRequiredConstructor(Class type, Object... ctorArgs)
            throws NoSuchMethodException {

        Supplier<NoSuchMethodException> notFoundException = () -> {
            return new NoSuchMethodException(String.format(
                    "No public contructor in %s for %s arguments", type,
                    Arrays.deepToString(ctorArgs)));
        };

        if (Stream.of(ctorArgs).allMatch(Objects::nonNull)) {
            // No `null` in constructor args, take easy path
            try {
                return type.getConstructor(Stream.of(ctorArgs).map(
                        Object::getClass).collect(Collectors.toList()).toArray(
                        Class[]::new));
            } catch (NoSuchMethodException ex) {
                // Failed to find ctor that can take the given arguments.
                Constructor varArgCtor = findVarArgConstructor(type);
                if (varArgCtor != null) {
                    // There is one with variable number of arguments. Use it.
                    return varArgCtor;
                }
                throw notFoundException.get();
            }
        }

        List<Constructor> ctors = Stream.of(type.getConstructors())
                .filter(ctor -> ctor.getParameterCount() == ctorArgs.length)
                .collect(Collectors.toList());

        if (ctors.isEmpty()) {
            // No public constructors that can handle the given arguments.
            throw notFoundException.get();
        }

        if (ctors.size() == 1) {
            return ctors.iterator().next();
        }

        // Revisit this tricky case when it will start bothering.
        throw notFoundException.get();
    }

    @Override
    public void accept(Object thiz) throws Throwable {
        method.invoke(thiz, methodArgs);
    }

    private final Object[] methodArgs;
    private final Method method;
    private final Object[] ctorArgs;

    final static Object[] DEFAULT_CTOR_ARGS = new Object[0];
}