1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
|
// SPDX-License-Identifier: BSD-3-Clause
package org.islandoftex.texplate.model;
import io.vavr.control.Either;
import org.islandoftex.texplate.util.MergingUtils;
import org.islandoftex.texplate.util.MessageUtils;
import org.islandoftex.texplate.util.PathUtils;
import org.islandoftex.texplate.util.ValidatorUtils;
import picocli.CommandLine;
import picocli.CommandLine.Option;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* The template processing class.
*
* @version 1.0
* @since 1.0
*/
@CommandLine.Command(
usageHelpWidth = 70,
name = "texplate"
)
public class TemplateProcessing implements Callable<Integer> {
// the file output, which will hold the
// result of the merging of both template
// and context data map from command line
@Option(
names = {"-o", "--output"},
description = "The output file in which the chosen "
+ "template will be effectively written. Make sure "
+ "the directory has the correct permissions for "
+ "writing the output file.",
required = true,
type = Path.class
)
private Path output;
// the template name
@Option(
names = {"-t", "--template"},
description = "The template and. The tool will "
+ "search both user and system locations and set the "
+ "template model accordingly, based on your specs."
)
private String template;
// the context data map that holds
// a set of key/value pairs to be
// merged with the template
@Option(
names = {"-m", "--map"},
description = "The contextual map that provides the "
+ "data to be merged in the template. This parameter "
+ "can be used multiple times. You can specify a map "
+ "entry with the key=value syntax (mind the entry "
+ "separator).",
arity = "1..*"
)
private Map<String, String> map;
@Option(
names = {"-c", "--config"},
description = "The configuration file in which the tool "
+ "can read template data, for automation purposes. Make "
+ "sure to follow the correct specification when writing "
+ "a configuration file.",
type = Path.class
)
private Path configuration;
/**
* The application logic, enclosed as a call method.
*
* @return An integer value denoting the exit status.
* @throws Exception An exception was raised in the application logic.
*/
@Override
public Integer call() throws Exception {
// the exit status, originally
// set as a valid value
int exit = 0;
// configuration halt flag, indicating
// whether the tool has to end earlier
boolean halt = false;
Map<String, Object> cmap = new HashMap<>();
// ensure the context data map
// is at least instantiated
ensureMap();
// there is a configuration file
// found in the command line
if (has(configuration)) {
// print the configuration
// check line for status
MessageUtils.line("Checking configuration");
// try to read the configuration
// file into a proper object
Either<Throwable, Configuration> configChecking = Configuration.
of(configuration).toEither();
// the configuration file
// seems to be valid, proceed
if (configChecking.isRight()) {
// print status line
MessageUtils.status(true);
// get the configuration file
Configuration config = configChecking.get();
// check if the configuration
// has a proper template
if (has(config.getTemplate())) {
// if so, build the template if, and
// only if, there's no one already
// set through command line
template = ensure(template, config.getTemplate());
}
// check if the configuration
// has a proper string/string map
if (has(config.getMap())) {
// set the main configuration
// map to be dealt later on
cmap = config.getMap();
}
// print header about tidying
// up configuration variables
MessageUtils.line("Adjusting variables from file");
MessageUtils.status(true);
System.out.println();
} else {
// an error occurred, print it
// set exit code and halt
MessageUtils.status(false);
MessageUtils.error(configChecking.getLeft());
exit = -1;
halt = true;
}
} else {
// print header regarding
// no config file found
MessageUtils.line("Configuration file mode disabled");
MessageUtils.status(true);
// print a header regarding
// full command line mode
MessageUtils.line("Entering full command line mode");
// there's no configuration file, so we
// need to check whether there is not a
// pattern set in the command line
if (!has(template)) {
// print status
MessageUtils.status(false);
// print message
MessageUtils.error(new Exception("The template was not set "
+ "in the command line through the -t/--template "
+ "option. If not explicitly specified in a "
+ "configuration file, this option becomes mandatory, "
+ "so make sure to define it either in the command "
+ "line or in a proper configuration file."));
exit = -1;
halt = true;
} else {
// print status
MessageUtils.status(true);
System.out.println();
}
}
// check whether we should
// halt prematurely
if (!halt) {
// initial message, preparing our
// hearts to the actual merging :)
System.out.println("Please, wait...");
System.out.println();
// now we need to obtain the actual
// template from a file stored either
// in the user home or in the system
MessageUtils.line("Obtaining reference");
// let us try to get the corresponding
// file from the template pattern
Either<Throwable, Path> fileChecking = PathUtils.
getTemplatePath(template).toEither();
// the actual template file was
// found, so we can proceed to
// the next phase
if (fileChecking.isRight()) {
// updates the current
// status accordingly
MessageUtils.status(true);
// now it's time to compose the template
// object from its corresponding file
MessageUtils.line("Composing template");
// attempts to retrieve the template
// attributes from the referenced file
// to the actual template object
Either<Throwable, Template> templateComposition
= Template.of(fileChecking.get()).toEither();
// the template composition was successful,
// so we can move on to the next phase
if (templateComposition.isRight()) {
// updates the current
// status accordingly
MessageUtils.status(true);
// once the template object is populated,
// we need to verify if both template and
// data map are not somehow conflicting
MessageUtils.line("Validating data");
Either<Throwable, Map<String, String>> dataValidation
= ValidatorUtils.validate(templateComposition.get(),
map).toEither();
// the data validation was consistent,
// so now the merging can be applied
if (dataValidation.isRight()) {
// updates the current
// status accordingly
MessageUtils.status(true);
// now it's the final phase, in which
// both template and data are merged
MessageUtils.line("Merging template and data");
// merge both template and context data
// map
Either<Throwable, Long> merging = MergingUtils.
merge(templateComposition.get(),
dataValidation.get(),
output, cmap).toEither();
// the merging was successful,
// so now there's nothing else
// to do, yay!
if (merging.isRight()) {
// updates the current
// status accordingly
MessageUtils.status(true);
// print the final messge and
// tell the user everything
// went smooth!
System.out.println();
System.out.println("Done! Enjoy your template!");
System.out.println("Written: "
+ getSize(merging.get()));
} else {
// updates the current
// status accordingly
MessageUtils.status(false);
// the merging failed, so the
// exception is displayed and
// the exit status is updated
MessageUtils.error(merging.getLeft());
exit = -1;
}
} else {
// updates the current
// status accordingly
MessageUtils.status(false);
// the data validation failed, so
// the exception is displayed and
// the exit status is updated
MessageUtils.error(dataValidation.getLeft());
exit = -1;
}
} else {
// updates the current
// status accordingly
MessageUtils.status(false);
// the template composition failed,
// so the exception is displayed and
// the exit status is updated
MessageUtils.error(templateComposition.getLeft());
exit = -1;
}
} else {
// updates the current
// status accordingly
MessageUtils.status(false);
// the file checking failed, so
// the exception is displayed and
// the exit status is updated
MessageUtils.error(fileChecking.getLeft());
exit = -1;
}
}
// the exit status is returned,
// denoting whether the application
// was able to merge both template
// and data accordingly
return exit;
}
/**
* Ensures the data map is never pointed to a null reference.
*/
private void ensureMap() {
// if the map is null, simply
// create a new instance
if (!has(map)) {
map = new HashMap<>();
}
}
/**
* Gets the file size in a human readable format.
*
* @param bytes The file size, in bytes.
* @return The file size in a human readable format.
*/
private String getSize(long bytes) {
if (bytes < 1024) {
return bytes + " B";
} else {
int exponent = (int) (Math.log(bytes) / Math.log(1024));
return String.format("%.1f %cB", bytes / Math.pow(1024, exponent),
"KMGTPE".charAt(exponent - 1));
}
}
/**
* Ensures the first parameter is not null, or sets it to the second one.
*
* @param <T> The type.
* @param first First parameter.
* @param second Second parameter.
* @return Either the first or the second one.
*/
private <T> T ensure(T first, T second) {
return !has(first) ? second : first;
}
/**
* Checks whether the object exists.
*
* @param object The objects.
* @return Boolean value indicating whether the object exists.
*/
private boolean has(Object object) {
return object != null;
}
}
|