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}