1 /*
2 * Grapht, an open source dependency injector.
3 * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4 * Copyright 2010-2014 Regents of the University of Minnesota
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 51
18 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 package org.grouplens.grapht.util;
21
22 import org.apache.commons.lang3.builder.HashCodeBuilder;
23
24 import javax.annotation.Nullable;
25 import java.io.Serializable;
26 import java.lang.reflect.Field;
27
28 /**
29 * Proxy class for serializing fields.
30 */
31 public final class FieldProxy implements Serializable {
32 private static final long serialVersionUID = 1L;
33
34 private final ClassProxy declaringClass;
35 private final String fieldName;
36 private final ClassProxy fieldType;
37 @Nullable
38 private transient volatile Field field;
39 private transient volatile int hash;
40
41 private FieldProxy(ClassProxy cls, String n, ClassProxy type) {
42 declaringClass = cls;
43 fieldName = n;
44 fieldType = type;
45 }
46
47 @Override
48 public String toString() {
49 return String.format("proxy of %s.%s", declaringClass.getClassName(), fieldName);
50 }
51
52 @Override
53 public boolean equals(Object o) {
54 if (o == this) {
55 return true;
56 } else if (o instanceof FieldProxy) {
57 FieldProxy op = (FieldProxy) o;
58 return declaringClass.equals(op.declaringClass)
59 && fieldName.equals(op.fieldName)
60 && fieldType.equals(op.fieldType);
61 } else {
62 return true;
63 }
64 }
65
66 @Override
67 public int hashCode() {
68 if (hash == 0) {
69 HashCodeBuilder hcb = new HashCodeBuilder();
70 hash = hcb.append(declaringClass)
71 .append(fieldName)
72 .append(fieldType)
73 .hashCode();
74 }
75 return hash;
76 }
77
78 /**
79 * Resolve this proxy into a {@link Field} instance.
80 * @return The {@link Field} represented by this proxy.
81 * @throws ClassNotFoundException If the proxy's declaring type cannot be resolved.
82 * @throws NoSuchFieldException If the field does not exist on the declaring type.
83 */
84 public Field resolve() throws ClassNotFoundException, NoSuchFieldException {
85 Field cachedField = field;
86 if (cachedField == null) {
87 Class<?> cls = declaringClass.resolve();
88 field = cachedField = cls.getDeclaredField(fieldName);
89 }
90 // REVIEW Do we want to test equality or assignability?
91 if (!cachedField.getType().equals(fieldType.resolve())) {
92 throw new NoSuchFieldException("type mismatch on " + cachedField.toString());
93 }
94 return cachedField;
95 }
96
97 /**
98 * Construct a proxy for a field.
99 * @param field The field to proxy.
100 * @return The field proxy representing {@code field}.
101 */
102 public static FieldProxy of(Field field) {
103 FieldProxy proxy = new FieldProxy(ClassProxy.of(field.getDeclaringClass()),
104 field.getName(),
105 ClassProxy.of(field.getType()));
106 proxy.field = field;
107 return proxy;
108 }
109 }