1 package guru.mikelue.jdut.yaml.node;
2
3 import java.sql.Connection;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Optional;
8 import java.util.stream.Collectors;
9
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
12 import org.apache.commons.lang3.Validate;
13 import org.apache.commons.lang3.builder.EqualsBuilder;
14 import org.apache.commons.lang3.builder.HashCodeBuilder;
15
16 import guru.mikelue.jdut.ConductorConfig;
17 import guru.mikelue.jdut.DataConductException;
18 import guru.mikelue.jdut.DataConductor;
19 import guru.mikelue.jdut.DuetFunctions;
20 import guru.mikelue.jdut.datagrain.DataField;
21 import guru.mikelue.jdut.datagrain.DataGrain;
22 import guru.mikelue.jdut.decorate.DataGrainDecorator;
23 import guru.mikelue.jdut.jdbc.JdbcFunction;
24 import guru.mikelue.jdut.operation.DataGrainOperator;
25 import guru.mikelue.jdut.yaml.LoadingYamlException;
26
27
28
29
30
31 public class TableNode implements NodeBase {
32
33
34
35 public final static class TableName {
36 private final String name;
37
38 public TableName(String newName)
39 {
40 name = newName;
41 }
42
43 @Override
44 public int hashCode()
45 {
46 return new HashCodeBuilder(996547137, 439619991)
47 .append(name)
48 .toHashCode();
49 }
50
51 @Override
52 public boolean equals(Object obj)
53 {
54 if (obj == null) { return false; }
55 if (obj == this) { return true; }
56 if (obj.getClass() != getClass()) {
57 return false;
58 }
59
60 TableName rhs = (TableName)obj;
61 return new EqualsBuilder()
62 .append(this.name, rhs.name)
63 .isEquals();
64 }
65 }
66
67 private final static class Config {
68 private Config() {}
69
70 private Optional<String> buildOperation = Optional.empty();
71 private Optional<String> cleanOperation = Optional.empty();
72 }
73
74 private Logger logger = LoggerFactory.getLogger(TableNode.class);
75 private List<?> dataRows = Collections.emptyList();
76 private List<String> columns = Collections.emptyList();
77 private List<String> keys = Collections.emptyList();
78 private Optional<String> decorator = Optional.empty();
79 private Config config = new Config();
80 private TableName tableName;
81
82 @SuppressWarnings("unchecked")
83 public TableNode(Object tableNode)
84 {
85 Map<TableName, ?> unknownTable = ((Map<TableName, ?>)tableNode);
86
87 tableName = unknownTable.keySet().stream().findFirst().get();
88 setData(unknownTable.get(tableName));
89 }
90
91
92
93
94
95
96
97
98
99
100 @SuppressWarnings("unchecked")
101 public DuetFunctions toDuetFunctions(
102 DataConductor dataConductor, ConductorConfig conductorConfig,
103 ConfigNode configNode
104 ) {
105 DataGrain dataGrain = DataGrain.build(
106 tableBuilder -> tableBuilder
107 .name(tableName.name)
108 .keys(keys.toArray(new String[0])),
109 dataBuilder -> {
110 dataBuilder.implicitColumns(columns.toArray(new String[0]));
111
112 dataRows.forEach(
113 row -> {
114 if (List.class.isInstance(row)) {
115 dataBuilder.addValues(((List<Object>)row).toArray(new Object[0]));
116 return;
117 }
118 if (Map.class.isInstance(row)) {
119 dataBuilder.addFields(
120 ((Map<String, Object>)row).entrySet().stream()
121 .map(entry -> dataBuilder.newField(entry.getKey(), entry.getValue()))
122 .collect(Collectors.toList())
123 .toArray(new DataField<?>[0])
124 );
125 return;
126 }
127
128 throw new LoadingYamlException("Unknown type of \"data\"[%s] for table: %s", row.getClass(), tableName.name);
129 }
130 );
131 }
132 );
133
134 String nameOfBuilding = config.buildOperation.orElseGet(() -> configNode.getNameOfBuildOperator());
135 String nameOfCleaning = config.cleanOperation.orElseGet(() -> configNode.getNameOfCleanOperator());
136
137 logger.debug(
138 "Use [{}] for building, [{}] for cleaning, Table: [{}]",
139 nameOfBuilding, nameOfCleaning, tableName.name
140 );
141
142
143
144
145 DataGrainDecorator decoratorObject = rowBuilder -> {};
146 if (configNode.getDecorator().isPresent()) {
147 decoratorObject = decoratorObject.chain(
148 conductorConfig.getDecorator(configNode.getDecorator().get())
149 .orElseThrow(
150 () -> new LoadingYamlException("Cannot found decorator: \"%s\"", configNode.getDecorator().get())
151 )
152 );
153 }
154 if (decorator.isPresent()) {
155 decoratorObject = decoratorObject.chain(
156 conductorConfig.getDecorator(decorator.get())
157 .orElseThrow(
158 () -> new LoadingYamlException("Cannot found decorator: \"%s\"", configNode.getDecorator().get())
159 )
160 );
161 }
162
163
164 return new DuetFunctionsImplOfDataGrain(
165 dataConductor,
166 dataConductor.buildJdbcFunction(
167 dataGrain.decorate(decoratorObject),
168 conductorConfig.getOperator(nameOfBuilding).orElseThrow(
169 () -> new LoadingYamlException("Cannot found operator: \"%s\"", nameOfBuilding)
170 )
171 ),
172 conductorConfig.getOperator(nameOfCleaning).orElseThrow(
173 () -> new LoadingYamlException("Cannot found operator: \"%s\"", nameOfBuilding)
174 )
175 );
176 }
177
178 @Override
179 public NodeType getNodeType()
180 {
181 return NodeType.Table;
182 }
183
184 @SuppressWarnings("unchecked")
185 private void setData(Object unknownData)
186 {
187 logger.trace("Load table: {}", tableName.name);
188
189
190
191
192 if (List.class.isInstance(unknownData)) {
193 dataRows = (List<?>)unknownData;
194 return;
195 }
196
197
198 Map<String, ?> complexData = (Map<String, ?>)unknownData;
199 complexData.forEach(
200 (key, value) -> {
201 switch (key) {
202 case "columns":
203 Validate.isTrue(List.class.isInstance(value), "\"columns\" need to be !!seq");
204 columns = (List<String>)value;
205 break;
206 case "data":
207 Validate.isTrue(List.class.isInstance(value), "\"data\" need to be !!seq");
208 dataRows = (List<?>)value;
209 break;
210 case "keys":
211 Validate.isTrue(List.class.isInstance(value), "\"keys\" need to be !!seq");
212 keys = (List<String>)value;
213 break;
214 case "config":
215 Validate.isTrue(Map.class.isInstance(value), "\"config\" need to be !!map");
216 ((Map<String, String>)value).forEach(
217 (keyOfConfig, valueOfConfig) -> {
218 switch (keyOfConfig) {
219 case "build_operation":
220 config.buildOperation = Optional.of(valueOfConfig);
221 break;
222 case "clean_operation":
223 config.cleanOperation = Optional.of(valueOfConfig);
224 break;
225 case "decorator":
226 decorator = Optional.of((String)valueOfConfig);
227 break;
228 default:
229 throw new LoadingYamlException("Unknown property[%s] of \"config\" in table: \"%s\"", keyOfConfig, tableName.name);
230 }
231 }
232 );
233 break;
234 default:
235 throw new LoadingYamlException("Unknown property of \"%s\" in table: \"%s\"", key, tableName.name);
236 }
237 }
238 );
239 }
240 }
241
242 class DuetFunctionsImplOfDataGrain implements DuetFunctions {
243 private DataGrain cleanDataGrain;
244
245 private JdbcFunction<Connection, DataGrain> buildFunction;
246 private JdbcFunction<Connection, DataGrain> cleanFunction;
247
248 DuetFunctionsImplOfDataGrain(
249 DataConductor dataConductor,
250 JdbcFunction<Connection, DataGrain> newBuildFunction,
251 DataGrainOperator cleanOperator
252 ) {
253 buildFunction = conn -> {
254 DataGrain processedDataGrain = newBuildFunction.applyJdbc(conn)
255 .reverse();
256
257 cleanDataGrain = processedDataGrain.reverse();
258
259 return processedDataGrain;
260 };
261
262 cleanFunction = conn -> cleanOperator.operate(conn, cleanDataGrain);
263 }
264
265 @Override
266 public JdbcFunction<Connection, ?> getBuildFunction()
267 {
268 return buildFunction;
269 }
270
271 @Override
272 public JdbcFunction<Connection, ?> getCleanFunction()
273 {
274 if (cleanDataGrain == null) {
275 throw new DataConductException("The data grain has not been used for building before used for cleaning");
276 }
277
278 return cleanFunction;
279 }
280 }