    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}