afterUnmarshal(Unmarshaller,Object)   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 12
dl 0
loc 16
ccs 0
cts 7
cp 0
crap 6
rs 9.8
c 1
b 0
f 0
1
/*
2
 * Copyright 2014, Armenak Grigoryan, and individual contributors as indicated
3
 * by the @authors tag. See the copyright.txt in the distribution for a
4
 * full listing of individual contributors.
5
 *
6
 * This is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as
8
 * published by the Free Software Foundation; either version 2.1 of
9
 * the License, or (at your option) any later version.
10
 *
11
 * This software is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
 * Lesser General Public License for more details.
15
 */
16
package com.strider.datadefender.requirement.plan;
17
18
import com.strider.datadefender.requirement.Column;
19
import com.strider.datadefender.requirement.TypeConverter;
20
import java.lang.reflect.InvocationTargetException;
21
import java.lang.reflect.Method;
22
import java.sql.SQLException;
23
import java.util.List;
24
import java.util.Objects;
25
26
import javax.xml.bind.Unmarshaller;
27
import javax.xml.bind.annotation.XmlAccessType;
28
import javax.xml.bind.annotation.XmlAccessorType;
29
import javax.xml.bind.annotation.XmlAttribute;
30
import javax.xml.bind.annotation.XmlElement;
31
import javax.xml.bind.annotation.XmlIDREF;
32
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
33
34
import org.apache.commons.collections4.CollectionUtils;
35
import org.apache.commons.lang3.ObjectUtils;
36
import org.apache.commons.lang3.StringUtils;
37
import org.apache.commons.text.StringEscapeUtils;
38
39
import lombok.extern.log4j.Log4j2;
40
import lombok.Data;
41
42
/**
43
 *
44
 * @author Zaahid Bateson
45
 */
46
@Log4j2
47
@Data
48
@XmlAccessorType(XmlAccessType.NONE)
49
public class Plan implements Invokable {
50
51
    @XmlJavaTypeAdapter(FunctionAttributeAdapter.class)
52
    @XmlAttribute
53
    private Function combiner;
54
55
    @XmlAttribute(name = "combiner-glue")
56
    private String combinerGlue;
57
58
    private Object combinerGlueObject;
59
60
    @XmlElement(name = "function")
61
    private List<Function> functions;
62
63
    /**
64
     * Returns the Class of the first function's dynamic argument if one is set,
65
     * or null otherwise.
66
     *
67
     * @return
68
     */
69
    public Class<?> getDynamicArgumentType() {
70
        if (CollectionUtils.size(functions) > 0) {
71
            return CollectionUtils.emptyIfNull(functions.get(0).getArguments())
72
                .stream()
73
                .filter((a) -> Objects.equals(Boolean.TRUE, a.getIsDynamicValue()))
74
                .map((a) -> a.getType())
75
                .findFirst()
76
                .orElse(null);
77
        }
78
        return null;
79
    }
80
81
    /**
82
     * Invokes the combiner with the given arguments.
83
     *
84
     * If the combiner is non-static, assumes the combiner is a method defined
85
     * on firstArgs' class, and so calls it on firstArg, passing secondArg as an
86
     * argument.
87
     *
88
     * @param firstArg
89
     * @param secondArg
90
     * @return
91
     * @throws SQLException
92
     * @throws IllegalAccessException
93
     * @throws InvocationTargetException
94
     * @throws InstantiationException
95
     */
96
    protected Object invokeCombiner(Object firstArg, Object secondArg)
97
        throws SQLException,
98
        IllegalAccessException,
99
        InvocationTargetException,
100
        InstantiationException {
101
102
        List<Argument> args = combiner.getArguments();
103
        int index = 0;
104
        if (combiner.isStatic()) {
105
            args.get(index++).setObjectValue(firstArg);
106
        }
107
        args.get(index).setObjectValue(secondArg);
108
        return combiner.invoke(firstArg);
109
    }
110
111
    /**
112
     * Chains the underlying functions, applying "Combiner" if set.
113
     *
114
     * @param runningValue
115
     * @return
116
     * @throws SQLException
117
     * @throws IllegalAccessException
118
     * @throws InvocationTargetException
119
     * @throws InstantiationException
120
     */
121
    @Override
122
    public Object invoke(Object runningValue)
123
        throws SQLException,
124
        IllegalAccessException,
125
        InvocationTargetException,
126
        InstantiationException {
127
128
        boolean isFirst = true;
129
        Object glue = null;
130
        for (Function fn : functions) {
131
            log.debug("Invoking function: {}", fn.getFunction());
132
            Object returnValue = fn.invoke(runningValue);
133
            log.debug("runningValue = " + runningValue);
134
            log.debug("returnValue = " + returnValue);
135
            if (combiner != null && !isFirst) {
136
                final Object gl = glue;
137
                log.debug(
138
                    "Combining with: {}, using glue: \"{}\"",
139
                    () -> combiner.getFunctionName(),
140
                    () -> StringEscapeUtils.escapeJava(Objects.toString(gl))
141
                );
142
                if (gl != null) {
143
                    runningValue = invokeCombiner(runningValue, gl);
144
                }
145
                returnValue = invokeCombiner(runningValue, returnValue);
146
            }
147
            glue = ObjectUtils.firstNonNull(
148
                fn.getCombinerGlueObject(),
149
                combinerGlueObject
150
            );
151
            runningValue = returnValue;
152
            isFirst = false;
153
        }
154
        return runningValue;
155
    }
156
157
    protected void initialize(Class<?> columnType)
158
        throws ClassNotFoundException,
159
        InstantiationException,
160
        IllegalAccessException,
161
        InvocationTargetException {
162
163
        log.debug("Initializing plan with column type: {}", columnType);
164
        Method cm = null;
165
        if (combiner != null) {
166
            log.debug(
167
                "Found combiner: {}, invoking for type: {}",
168
                () -> combiner.getFunctionName(),
169
                () -> columnType
170
            );
171
            combiner.setArguments(List.of(new Argument(columnType), new Argument(columnType)));
172
            cm = combiner.initialize(columnType);
173
            if (!StringUtils.isEmpty(combinerGlue)) {
174
                log.debug("Found combiner glue: \"{}\"", () -> StringEscapeUtils.escapeJava(combinerGlue));
175
                combinerGlueObject = TypeConverter.convert(combinerGlue, columnType);
176
                log.debug("combinerGlueObject: \"{}\"", () -> StringEscapeUtils.escapeJava(Objects.toString(combinerGlueObject)));
177
            }
178
        }
179
180
        for (Function fn : CollectionUtils.emptyIfNull(functions)) {
181
            Method m = fn.initialize(columnType);
182
            Class<?> rtype = m.getReturnType();
183
            if (cm != null && !TypeConverter.isConvertible(rtype, cm.getParameterTypes()[0])) {
184
                log.debug(
185
                    "The combiner {} cannot be called with the return type: {} of one of the methods in the chain: {}",
186
                    combiner.getFunctionName(),
187
                    rtype.getName(),
188
                    m.getName()
189
                );
190
                throw new IllegalArgumentException("Combiner: " + combiner.getFunctionName() + " can't be called for function: " + m.getName());
191
            }
192
        }
193
    }
194
195
    /**
196
     * Uses functionName and parameters to find the method to associate with
197
     * 'Function'.
198
     *
199
     * @param unmarshaller
200
     * @param parent
201
     * @throws ClassNotFoundException
202
     * @throws InstantiationException
203
     * @throws IllegalAccessException
204
     * @throws InvocationTargetException
205
     */
206
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent)
207
        throws ClassNotFoundException,
208
        InstantiationException,
209
        IllegalAccessException,
210
        InvocationTargetException {
211
212
        if (!(parent instanceof Column)) {
213
            return;
214
        }
215
216
        log.debug("Unmarshalling plan for column {}", ((Column) parent).getName());
217
        log.debug("combiner-glue: {}", combinerGlue);
218
        log.debug("Function count: {}", () -> CollectionUtils.size(functions));
219
220
        final Class<?> columnType = ((Column) parent).getType();
221
        initialize(columnType);
222
    }
223
}
224