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 */
017
018package org.apache.commons.jexl3.internal.introspection;
019
020import java.lang.reflect.Field;
021import java.lang.reflect.Modifier;
022import org.apache.commons.jexl3.introspection.JexlPropertySet;
023
024/**
025 * A JexlPropertySet for public fields.
026 */
027public final class FieldSetExecutor implements JexlPropertySet {
028    /**
029     * The public field.
030     */
031    private final Field field;
032
033    /**
034     * Attempts to discover a FieldSetExecutor.
035     *
036     * @param is the introspector
037     * @param clazz the class to find the get method from
038     * @param identifier the key to use as an argument to the get method
039     * @param value the value to set the field to
040     * @return the executor if found, null otherwise
041     */
042    public static JexlPropertySet discover(final Introspector is,
043                                           final Class<?> clazz,
044                                           final String identifier,
045                                           final Object value) {
046        if (identifier != null) {
047            final Field field = is.getField(clazz, identifier);
048            if (field != null
049                && !Modifier.isFinal(field.getModifiers())
050                && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
051                return new FieldSetExecutor(field);
052            }
053        }
054        return null;
055    }
056
057    /**
058     * Creates a new instance of FieldPropertySet.
059     * @param theField the class public field
060     */
061    private FieldSetExecutor(final Field theField) {
062        field = theField;
063    }
064
065    @Override
066    public Object invoke(final Object obj, final Object arg) throws Exception {
067        field.set(obj, arg);
068        return arg;
069    }
070
071    @Override
072    public Object tryInvoke(final Object obj, final Object key, final Object value) {
073        if (obj.getClass().equals(field.getDeclaringClass())
074            && key.equals(field.getName())
075            && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
076            try {
077                field.set(obj, value);
078                return value;
079            } catch (final IllegalAccessException xill) {
080                return Uberspect.TRY_FAILED;
081            }
082        }
083        return Uberspect.TRY_FAILED;
084    }
085
086    @Override
087    public boolean tryFailed(final Object rval) {
088        return rval == Uberspect.TRY_FAILED;
089    }
090
091    @Override
092    public boolean isCacheable() {
093        return true;
094    }
095}