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.util.List;
020import java.lang.reflect.Array;
021
022/**
023 * Specialized executor to set a property in a List or array.
024 * @since 2.0
025 */
026public final class ListSetExecutor extends AbstractExecutor.Set {
027    /** The java.lang.reflect.Array.get method used as an active marker in ListGet. */
028    private static final java.lang.reflect.Method ARRAY_SET =
029            initMarker(Array.class, "set", Object.class, Integer.TYPE, Object.class);
030    /** The java.util.obj.set method used as an active marker in ListSet. */
031    private static final java.lang.reflect.Method LIST_SET =
032            initMarker(List.class, "set", Integer.TYPE, Object.class);
033    /** The property. */
034    private final Integer property;
035
036    /**
037     * Attempts to discover a ListSetExecutor.
038     *
039     * @param is the introspector
040     * @param clazz the class to find the get method from
041     * @param identifier the key to use as an argument to the get method
042     * @param value the value to use as argument in list.put(key,value)
043     * @return the executor if found, null otherwise
044     */
045    public static ListSetExecutor discover(final Introspector is,
046                                           final Class<?> clazz,
047                                           final Object identifier,
048                                           final Object value) {
049        final Integer index = castInteger(identifier);
050        if (index != null) {
051            if (clazz.isArray()) {
052                // we could verify if the call can be performed but it does not change
053                // the fact we would fail...
054                // Class<?> formal = clazz.getComponentType();
055                // Class<?> actual = value == null? Object.class : value.getClass();
056                // if (IntrospectionUtils.isMethodInvocationConvertible(formal, actual, false)) {
057                return new ListSetExecutor(clazz, ARRAY_SET, index);
058                // }
059            }
060            if (List.class.isAssignableFrom(clazz)) {
061                return new ListSetExecutor(clazz, LIST_SET, index);
062            }
063        }
064        return null;
065    }
066
067    /**
068     * Creates an instance.
069     *
070     * @param clazz the class the set method applies to
071     * @param method the method called through this executor
072     * @param key the key to use as 1st argument to the set method
073     */
074    private ListSetExecutor(final Class<?> clazz, final java.lang.reflect.Method method, final Integer key) {
075        super(clazz, method);
076        property = key;
077    }
078
079    @Override
080    public Object getTargetProperty() {
081        return property;
082    }
083
084    @Override
085    public Object invoke(final Object obj, final Object value) {
086        if (method == ARRAY_SET) {
087            Array.set(obj, property, value);
088        } else {
089            @SuppressWarnings("unchecked") // LSE should only be created for array or list types
090            final List<Object> list = (List<Object>) obj;
091            list.set(property, value);
092        }
093        return value;
094    }
095
096    @Override
097    public Object tryInvoke(final Object obj, final Object key, final Object value) {
098        final Integer index = castInteger(key);
099        if (obj != null && method != null
100                && objectClass.equals(obj.getClass())
101                && index != null) {
102            if (method == ARRAY_SET) {
103                Array.set(obj, index, value);
104            } else {
105                @SuppressWarnings("unchecked")  // LSE should only be created for array or list types
106                final List<Object> list = (List<Object>) obj;
107                list.set(index, value);
108            }
109            return value;
110        }
111        return TRY_FAILED;
112    }
113}