1/*
2 * Copyright (c) 2007 Mockito contributors
3 * This program is made available under the terms of the MIT License.
4 */
5package org.mockito.internal.util.reflection;
6
7import org.mockito.internal.util.Checks;
8
9import java.lang.annotation.Annotation;
10import java.lang.reflect.Field;
11
12/**
13 * Represents an accessible instance field.
14 *
15 * Contains the instance reference on which the field can be read adn write.
16 */
17public class InstanceField {
18    private final Field field;
19    private final Object instance;
20    private FieldReader fieldReader;
21
22    /**
23     * Create a new InstanceField.
24     *
25     * @param field The field that should be accessed, note that no checks are performed to ensure
26     *              the field belong to this instance class.
27     * @param instance The instance from which the field shall be accessed.
28     */
29    public InstanceField(Field field, Object instance) {
30        this.field = Checks.checkNotNull(field, "field");
31        this.instance = Checks.checkNotNull(instance, "instance");
32    }
33
34    /**
35     * Safely read the field.
36     *
37     * @return the field value.
38     * @see FieldReader
39     */
40    public Object read() {
41        return reader().read();
42    }
43
44    /**
45     * Set the given value to the field of this instance.
46     *
47     * @param value The value that should be written to the field.
48     * @see FieldSetter
49     */
50    public void set(Object value) {
51        new FieldSetter(instance, field).set(value);
52    }
53
54    /**
55     * Check that the field is not null.
56     *
57     * @return <code>true</code> if <code>null</code>, else <code>false</code>.
58     */
59    public boolean isNull() {
60        return reader().isNull();
61    }
62
63    /**
64     * Check if the field is annotated by the given annotation.
65     *
66     * @param annotationClass The annotation type to check.
67     * @return <code>true</code> if the field is annotated by this annotation, else <code>false</code>.
68     */
69    public boolean isAnnotatedBy(Class<? extends Annotation> annotationClass) {
70        return field.isAnnotationPresent(annotationClass);
71    }
72
73    /**
74     * Returns the annotation instance for the given annotation type.
75     *
76     * @param annotationClass Tha annotation type to retrieve.
77     * @param <A> Type of the annotation.
78     * @return The annotation instance.
79     */
80    public <A extends Annotation> A annotation(Class<A> annotationClass) {
81        return field.getAnnotation(annotationClass);
82    }
83
84    /**
85     * Returns the JDK {@link Field} instance.
86     *
87     * @return The actual {@link Field} instance.
88     */
89    public Field jdkField() {
90        return field;
91    }
92
93    private FieldReader reader() {
94        if (fieldReader == null) {
95            fieldReader = new FieldReader(instance, field);
96        }
97        return fieldReader;
98    }
99
100    /**
101     * Returns the name of the field.
102     *
103     * @return Name of the field.
104     */
105    public String name() {
106        return field.getName();
107    }
108
109    @Override
110    public boolean equals(Object o) {
111        if (this == o) return true;
112        if (o == null || getClass() != o.getClass()) return false;
113
114        InstanceField that = (InstanceField) o;
115
116        if (!field.equals(that.field)) return false;
117        if (!instance.equals(that.instance)) return false;
118
119        return true;
120    }
121
122    @Override
123    public int hashCode() {
124        int result = field.hashCode();
125        result = 31 * result + instance.hashCode();
126        return result;
127    }
128}
129