package net.minidev.asm;

/*
 *    Copyright 2011-2024 JSON-SMART authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;

/**
 * Contains all information needed to access a java field.
 * 
 * field, getter setter
 * 
 * this object is used internally by BeansAccess
 * 
 * @see BeansAccess
 * 
 * @author Uriel Chemouni
 */
public class Accessor {
	/**
	 * Field to access
	 */
	protected Field field;
	/**
	 * Setter Methods if available
	 */
	protected Method setter;
	/**
	 * getter Methods if available
	 */
	protected Method getter;
	/**
	 * Filed index in object
	 */
	protected int index;
	/**
	 * Filed Class
	 */
	protected Class<?> type;
	/**
	 * Filed Type using JDK 5+ generics if available
	 */
	protected Type genericType;
	/**
     * The name of the field.
     */
	protected String fieldName;

	/**
	 * getter for index
	 * @return Index
	 */
	public int getIndex() {
		return index;
	}

	/**
	 * is the field access using Field access type
	 * @return if Accessor is public
	 */
	public boolean isPublic() {
		return setter == null && getter == null;
	}

	/**
	 * is the field is an enum field
	 * @return if Accessor return an Enum Class
	 */
	public boolean isEnum() {
		return type.isEnum();
	}

	/**
	 * return the field name
 	 * @return the field name
	 */
	public String getName() {
		return fieldName;
	}

	/**
	 * return field Class
	 * @return field Class
	 */
	public Class<?> getType() {
		return type;
	}

	/**
	 * return generics field Type.
	 * @return generics field Type.
	 */
	public Type getGenericType() {
		return genericType;
	}

	/**
	 * Determines if the field is accessible for reading or writing operations.
	 * 
	 * @return true if the field can be read or write
	 */
	public boolean isUsable() {
		return field != null || getter != null || setter != null;
	}

	/**
	 * Checks if the field is readable, either directly or through a getter method.
     *
	 * @return true if the field can be read
	 */
	public boolean isReadable() {
		return field != null || getter != null;
	}

	/**
	 * Determines if the field is writable, either directly or through a setter method.
     *
	 * @return true if the field can be write
	 */
	public boolean isWritable() {
		return field != null || setter != null;
	}

	/**
	 * build accessor for a field
	 * 
	 * @param c      the handled class
	 * @param field  the field to access
	 * @param filter field filter
	 */
	public Accessor(Class<?> c, Field field, FieldFilter filter) {
		this.fieldName = field.getName();
		int m = field.getModifiers();

		if ((m & (Modifier.STATIC | Modifier.TRANSIENT)) > 0)
			return;

		if ((m & Modifier.PUBLIC) > 0)
			this.field = field;

		String name = ASMUtil.getSetterName(field.getName());
		try {
			setter = c.getDeclaredMethod(name, field.getType());
		} catch (Exception e) {
		}
		boolean isBool = field.getType().equals(Boolean.TYPE);
		if (isBool) {
			name = ASMUtil.getIsName(field.getName());
		} else {
			name = ASMUtil.getGetterName(field.getName());
		}
		try {
			getter = c.getDeclaredMethod(name);
		} catch (Exception e) {
		}
		if (getter == null && isBool) {
			try {
				getter = c.getDeclaredMethod(ASMUtil.getGetterName(field.getName()));
			} catch (Exception e) {
			}
		}

		if (this.field == null && getter == null && setter == null)
			return;

		if (getter != null && !filter.canUse(field, getter))
			getter = null;

		if (setter != null && !filter.canUse(field, setter))
			setter = null;

		// no access disable
		if (getter == null && setter == null && this.field == null)
			return;

		this.type = field.getType();
		this.genericType = field.getGenericType();
	}
}
