defineDefaultField(MapperHydrator,Field)   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
dl 0
loc 6
ccs 2
cts 3
cp 0.6667
crap 2.1481
rs 10
1
/*
2
 * This file is part of ArakneLangLoader.
3
 *
4
 * ArakneLangLoader is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * ArakneLangLoader is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with ArakneLangLoader.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2020 Vincent Quatrevieux
18
 */
19
20
package fr.arakne.swflangloader.parser.mapper;
21
22
import fr.arakne.swflangloader.parser.Assignation;
23
import fr.arakne.swflangloader.parser.AssignationParser;
24
25
import java.lang.reflect.Field;
26
import java.lang.reflect.ParameterizedType;
27
import java.util.HashMap;
28
import java.util.Map;
29
30
/**
31
 * Hydrator for an SWF structure using mapped properties
32
 *
33
 * @param <T> The target structure type
34
 */
35 1
final public class MapperHydrator<T> {
36 1
    final private AssignationParser parser = new AssignationParser();
37 1
    final private Map<String, PropertyHydrator<T, ?>> hydrators = new HashMap<>();
38
    private PropertyHydrator<T, Map<String, Object>> defaultHydrator;
39
40
    /**
41
     * Define the default property (i.e. for undeclared variable) hydrator
42
     */
43
    public void setDefaultHydrator(PropertyHydrator<T, Map<String, Object>> defaultHydrator) {
44 1
        this.defaultHydrator = defaultHydrator;
45 1
    }
46
47
    /**
48
     * Define a property for a simple variable
49
     *
50
     * @param varName The SWF variable name
51
     * @param type The variable type
52
     * @param hydrator Hydrator to use
53
     *
54
     * @param <V> The variable type
55
     */
56
    public <V> void declareSimpleProperty(String varName, Class<V> type, PropertyHydrator<T, V> hydrator) {
57 1
        parser.declareSimple(varName, type);
58 1
        hydrators.put(varName, hydrator);
59 1
    }
60
61
    /**
62
     * Define a property for a map variable
63
     *
64
     * @param varName The SWF variable name
65
     * @param keyType The map key type
66
     * @param type The map value type
67
     * @param hydrator Hydrator to use
68
     *
69
     * @param <V> The variable type
70
     */
71
    public <K, V> void declareMapProperty(String varName, Class<K> keyType, Class<V> type, PropertyHydrator<T, Map<K, V>> hydrator) {
72 1
        parser.declareMap(varName, keyType, type);
73 1
        hydrators.put(varName, hydrator);
74 1
    }
75
76
    /**
77
     * Hydrator the target structure using the given action script line
78
     *
79
     * @param target The structure to hydrate
80
     * @param line The action script line
81
     */
82
    public void hydrate(T target, String line) {
83 1
        Assignation assignation = parser.parseLine(line);
84
85 1
        if (assignation.isNull()) {
86 1
            return;
87
        }
88
89 1
        if (!hydrators.containsKey(assignation.variableName())) {
90 1
            hydrateUndeclared(target, assignation);
91 1
            return;
92
        }
93
94
        @SuppressWarnings("unchecked")
95 1
        PropertyHydrator<T, Object> hydrator = (PropertyHydrator<T, Object>) hydrators.get(assignation.variableName());
96
97 1
        if (!assignation.isAssociative()) {
98 1
            hydrator.set(target, assignation.value());
99 1
            return;
100
        }
101
102
        @SuppressWarnings("unchecked")
103 1
        Map<Object, Object> value = (Map<Object, Object>) hydrator.get(target);
104
105 1
        if (value == null) {
106
            value = new HashMap<>();
107
            hydrator.set(target, value);
108
        }
109
110 1
        value.put(assignation.key(), assignation.value());
111 1
    }
112
113
    @SuppressWarnings("unchecked")
114
    private void hydrateUndeclared(T target, Assignation assignation) {
115 1
        if (defaultHydrator == null) {
116 1
            return;
117
        }
118
119 1
        Map<String, Object> value = defaultHydrator.get(target);
120
121 1
        if (value == null) {
122 1
            value = new HashMap<>();
123 1
            defaultHydrator.set(target, value);
124
        }
125
126 1
        if (!assignation.isAssociative()) {
127 1
            value.put(assignation.variableName(), assignation.value());
128 1
            return;
129
        }
130
131 1
        final Object lastValue = value.get(assignation.variableName());
132
        Map<Object, Object> mapValue;
133
134 1
        if (!(lastValue instanceof Map)) {
135 1
            mapValue = new HashMap<>();
136 1
            value.put(assignation.variableName(), mapValue);
137
        } else {
138 1
            mapValue = (Map<Object, Object>) lastValue;
139
        }
140
141 1
        mapValue.put(assignation.key(), assignation.value());
142 1
    }
143
144
    /**
145
     * Create the hydrator instance by parsing annotations
146
     *
147
     * @param type The structure class
148
     * @param <T> The structure type
149
     *
150
     * @return The hydrator instance
151
     */
152
    static public <T> MapperHydrator<T> parseAnnotations(Class<T> type) {
153 1
        MapperHydrator<T> hydrator = new MapperHydrator<>();
154
155 1
        Class<?> current = type;
156
157 1
        while (current.getSuperclass() != null) {
158 1
            for (Field field : current.getDeclaredFields()) {
159 1
                if (field.isAnnotationPresent(SwfDefault.class)) {
160 1
                    defineDefaultField(hydrator, field);
161 1
                    continue;
162
                }
163
164 1
                SwfVariable annotation = field.getAnnotation(SwfVariable.class);
165
166 1
                if (annotation != null) {
167 1
                    defineSwfField(hydrator, field, annotation);
168
                }
169
            }
170
171 1
            current = current.getSuperclass();
172
        }
173
174 1
        return hydrator;
175
    }
176
177
    private static <T> void defineSwfField(MapperHydrator<T> hydrator, Field field, SwfVariable annotation) {
178 1
        final String varName = annotation.value().isEmpty() ? field.getName() : annotation.value();
179
180 1
        if (field.getType().equals(Map.class)) {
181 1
            ParameterizedType mapType = (ParameterizedType) field.getGenericType();
182
183 1
            hydrator.declareMapProperty(
184
                varName,
185 1
                (Class<?>) mapType.getActualTypeArguments()[0],
186 1
                (Class<?>) mapType.getActualTypeArguments()[1],
187
                new ReflectionPropertyHydrator<>(field)
188
            );
189 1
        } else {
190 1
            hydrator.declareSimpleProperty(varName, field.getType(), new ReflectionPropertyHydrator<>(field));
191
        }
192 1
    }
193
194
    private static <T> void defineDefaultField(MapperHydrator<T> hydrator, Field field) {
195 1
        if (!field.getType().equals(Map.class)) {
196
            throw new IllegalArgumentException("Default swf field must be declared as Map<String, Object>");
197
        }
198
199 1
        hydrator.setDefaultHydrator(new ReflectionPropertyHydrator<>(field));
200 1
    }
201
}
202