es.webbeta.serializer.FieldAccessor   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 100
dl 0
loc 147
c 2
b 0
f 0
rs 10
wmc 29

10 Methods

Rating   Name   Duplication   Size   Complexity  
A FieldAccessor(Object,String,FieldAccessType) 0 12 1
A buildGetterNames(String) 0 9 1
C buildAsMethod() 0 37 10
B buildAsProperty() 0 17 6
A exists() 0 4 1
A setCustomGetterName(String) 0 2 1
A setLogger(Logger) 0 2 1
A setEnsureFieldExists(Boolean) 0 2 1
A get() 0 5 1
B init() 0 19 6
1
package es.webbeta.serializer;
2
3
import com.google.common.collect.Lists;
4
import es.webbeta.serializer.base.Logger;
5
import es.webbeta.serializer.type.FieldAccessType;
6
7
import java.lang.reflect.Field;
8
import java.lang.reflect.InvocationTargetException;
9
import java.lang.reflect.Method;
10
import java.lang.reflect.Modifier;
11
import java.util.ArrayList;
12
import java.util.List;
13
14
public class FieldAccessor implements es.webbeta.serializer.base.FieldAccessor {
0 ignored issues
show
Comprehensibility introduced by
Class or interface names should not shadow other classes or interfaces. In general, shadowing is a bad practice as it makes code harder to understand. Consider renaming this class.
Loading history...
15
16
    private Boolean initialized = false;
17
18
    private Class<?> klass;
19
    private List<Class<?>> klassTree;
20
    private Object ob;
21
    private String fieldName;
22
    private FieldAccessType accessType;
23
24
    private Logger logger;
25
26
    private Boolean exists = false;
27
    private Object value = null;
28
29
    private Boolean ensureFieldExists = true;
30
    private String customGetterName;
31
32
    public FieldAccessor(
33
            Object ob,
34
            String fieldName,
35
            FieldAccessType accessType
36
    ) {
37
        this.ob = ob;
38
        this.fieldName = fieldName;
39
        this.accessType = accessType;
40
41
        klass = ob.getClass();
42
        klassTree = new ArrayList<>();
43
        klassTree.add(klass);
44
    }
45
46
    public void setLogger(Logger logger) {
47
        this.logger = logger;
48
    }
49
50
    private String[] buildGetterNames(String fieldName) {
51
        List<String> names = new ArrayList<>();
52
53
        String firstLetter = fieldName.substring(0, 1);
54
55
        names.add("get" + firstLetter.toUpperCase() + fieldName.substring(1));
56
        names.add("is" + firstLetter.toUpperCase() + fieldName.substring(1));
57
58
        return names.toArray(new String[0]);
59
    }
60
61
    private void init() {
62
        if (initialized) return;
63
64
        Class<?> superKlass = klass;
65
        while (superKlass != null) {
66
            superKlass = superKlass.getSuperclass();
67
            if (superKlass.getCanonicalName().equals(Object.class.getCanonicalName()))
68
                break;
69
70
            klassTree.add(superKlass);
71
        }
72
        klassTree = Lists.reverse(klassTree);
73
74
        if (accessType == FieldAccessType.PROPERTY)
75
            buildAsProperty();
76
        else if (accessType == FieldAccessType.PUBLIC_METHOD)
77
            buildAsMethod();
78
79
        initialized = true;
80
    }
81
82
    @SuppressWarnings("all")
83
    private void buildAsProperty() {
84
        for (Class<?> klass : klassTree) {
85
            try {
86
                Field field = klass.getDeclaredField(fieldName);
87
                if (!field.isAccessible())
88
                    field.setAccessible(true);
89
                value = field.get(ob);
90
                exists = true;
91
            } catch (IllegalAccessException e) {
92
                if (logger != null) {
93
                    logger.error(String.format("Serializer cannot serialize field '%s.%s', because it is not public.",
94
                            klass.getCanonicalName(),
95
                            fieldName
96
                    ));
97
                }
98
            } catch (NoSuchFieldException ignored) {}
99
        }
100
    }
101
102
    private void buildAsMethod() {
103
        if (!Modifier.isPublic(klass.getModifiers()))
104
            throw new IllegalAccessError("Serializer cannot access \"" + klass.getCanonicalName() + "\" class. It can be caused because it has non public access.");
105
106
        String[] names;
107
        if (customGetterName == null)
108
            names = buildGetterNames(fieldName);
109
        else
110
            names = new String[]{customGetterName};
111
112
        for (String name : names) {
113
            try {
114
                Method method = klass.getMethod(name);
115
                value = method.invoke(ob);
116
                exists = true;
117
                break;
118
            } catch (NoSuchMethodException ignored) {
119
                exists = false;
120
            } catch (IllegalAccessException | InvocationTargetException e) {
121
                if (logger != null) {
122
                    logger.error(String.format("Serializer cannot serialize method '%s.%s', because it %s",
123
                            klass.getCanonicalName(), name,
124
                            e instanceof IllegalAccessException ? "is not public." :
0 ignored issues
show
Best Practice introduced by
It is bad practice to use the instanceOf() operator to perform flow control in a catch block. Consider using multiple catch blocks instead.
Loading history...
125
                                    "throw an exception when was called."
126
                    ));
127
                }
128
            }
129
        }
130
131
        if (ensureFieldExists) {
132
            FieldAccessor accessorAsField =
133
                    new FieldAccessor(ob, fieldName, FieldAccessType.PROPERTY);
134
135
            exists = exists && accessorAsField.exists();
136
137
            if (!exists)
138
                value = null;
139
        }
140
    }
141
142
    public void setEnsureFieldExists(Boolean ensureFieldExists) {
143
        this.ensureFieldExists = ensureFieldExists;
144
    }
145
146
    public void setCustomGetterName(String customGetterName) {
147
        this.customGetterName = customGetterName;
148
    }
149
150
    @Override
151
    public Boolean exists() {
152
        init();
153
        return exists;
154
    }
155
156
    @SuppressWarnings("unchecked")
157
    @Override
158
    public <T> T get() {
159
        init();
160
        return (T) value;
161
    }
162
163
}
164