    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.jexl3.internal.introspection;
    018
    019import java.beans.IntrospectionException;
    020import java.lang.reflect.Constructor;
    021import java.lang.reflect.InvocationTargetException;
    022import org.apache.commons.jexl3.JexlException;
    023import org.apache.commons.jexl3.introspection.JexlMethod;
    024
    025/**
    026 * A JexlMethod that wraps a constructor.
    027 */
    028public final class ConstructorMethod implements JexlMethod {
    029    /** The wrapped constructor. */
    030    private final Constructor<?> ctor;
    031
    032    /**
    033     * Discovers a class constructor and wrap it as a JexlMethod.
    034     * @param is the introspector
    035     * @param ctorHandle a class or class name
    036     * @param args constructor arguments
    037     * @return a {@link JexlMethod}
    038     */
    039    public static ConstructorMethod discover(final Introspector is, final Object ctorHandle, final Object... args) {
    040        String className;
    041        Class<?> clazz = null;
    042        if (ctorHandle instanceof Class<?>) {
    043            clazz = (Class<?>) ctorHandle;
    044            className = clazz.getName();
    045        } else if (ctorHandle != null) {
    046            className = ctorHandle.toString();
    047        } else {
    048            return null;
    049        }
    050        final Constructor<?> ctor = is.getConstructor(clazz, new MethodKey(className, args));
    051        if (ctor != null) {
    052            return new ConstructorMethod(ctor);
    053        }
    054        return null;
    055    }
    056    /**
    057     * Creates a constructor method.
    058     * @param theCtor the constructor to wrap
    059     */
    060    ConstructorMethod(final Constructor<?> theCtor) {
    061        this.ctor = theCtor;
    062    }
    063
    064    @Override
    065    public Object invoke(final Object obj, final Object... params) throws Exception {
    066        final Class<?> ctorClass = ctor.getDeclaringClass();
    067        boolean invoke = true;
    068        if (obj != null) {
    069            if (obj instanceof Class<?>) {
    070                invoke = ctorClass.equals(obj);
    071            } else {
    072                invoke = ctorClass.getName().equals(obj.toString());
    073            }
    074        }
    075        if (invoke) {
    076                return ctor.newInstance(params);
    077            }
    078        throw new IntrospectionException("constructor resolution error");
    079    }
    080
    081    @Override
    082    public Object tryInvoke(final String name, final Object obj, final Object... params) {
    083        try {
    084            final Class<?> ctorClass = ctor.getDeclaringClass();
    085            boolean invoke = true;
    086            if (obj != null) {
    087                if (obj instanceof Class<?>) {
    088                    invoke = ctorClass.equals(obj);
    089                } else {
    090                    invoke = ctorClass.getName().equals(obj.toString());
    091                }
    092            }
    093            invoke &= name == null || ctorClass.getName().equals(name);
    094            if (invoke) {
    095                return ctor.newInstance(params);
    096            }
    097        } catch (InstantiationException | IllegalArgumentException | IllegalAccessException xinstance) {
    098            return Uberspect.TRY_FAILED;
    099        } catch (final InvocationTargetException xinvoke) {
    100            throw JexlException.tryFailed(xinvoke); // throw
    101        }
    102        return Uberspect.TRY_FAILED;
    103    }
    104
    105    @Override
    106    public boolean tryFailed(final Object rval) {
    107        return rval == Uberspect.TRY_FAILED;
    108    }
    109
    110    @Override
    111    public boolean isCacheable() {
    112        return true;
    113    }
    114
    115    @Override
    116    public Class<?> getReturnType() {
    117        return ctor.getDeclaringClass();
    118    }
    119
    120}