extractWorkoutItem(Sheet,int,int,ExcelGoal,String,List)   F
last analyzed

Complexity

Conditions 14

Size

Total Lines 127
Code Lines 117

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 117
dl 0
loc 127
rs 2.52
c 2
b 0
f 0
cc 14

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like com.osomapps.pt.xlsx.XlsxProgramParser.extractWorkoutItem(Sheet,int,int,ExcelGoal,String,List) often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
package com.osomapps.pt.xlsx;
2
3
import java.io.IOException;
4
import java.io.InputStream;
5
import java.util.ArrayList;
6
import java.util.List;
7
import java.util.Optional;
8
import java.util.StringJoiner;
9
import lombok.extern.slf4j.Slf4j;
10
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
11
import org.apache.poi.ss.usermodel.Cell;
12
import org.apache.poi.ss.usermodel.CellType;
13
import org.apache.poi.ss.usermodel.Row;
14
import org.apache.poi.ss.usermodel.Sheet;
15
import org.apache.poi.ss.usermodel.Workbook;
16
import org.apache.poi.ss.usermodel.WorkbookFactory;
17
18
@Slf4j
19
public class XlsxProgramParser {
20
    private final InputStream inputStream;
21
22
    private XlsxProgramParser(InputStream inputStream) {
23
        this.inputStream = inputStream;
24
    }
25
26
    public static XlsxProgramParser of(InputStream inputStream) {
27
        return new XlsxProgramParser(inputStream);
28
    }
29
30
    public ExcelSheets getExcelSheets() {
31
        final List<ExcelExercise> excelExercises = new ArrayList<>();
32
        final List<ExcelGoal> excelGoals = new ArrayList<>();
33
        try (final Workbook workbook = WorkbookFactory.create(inputStream)) {
34
35
            excelExercises.addAll(extractExercises(workbook.getSheetAt(3)));
36
            for (int index = 4; index < workbook.getNumberOfSheets(); index += 1) {
37
                final Sheet sheet = workbook.getSheetAt(index);
38
                ExcelGoal excelGoal = extractGoal(index, sheet, excelExercises);
39
                if (!excelGoal.getUserGroups().isEmpty()) {
40
                    excelGoals.add(excelGoal);
41
                }
42
            }
43
        } catch (IOException ex) {
44
            log.error(ex.getMessage(), ex);
45
        }
46
        return new ExcelSheets().setExcelExercises(excelExercises).setExcelGoals(excelGoals);
47
    }
48
49
    private List<ExcelExercise> extractExercises(Sheet sheet) {
50
        List<ExcelExercise> excelExercises = new ArrayList<>();
51
        for (int exerciseIndex = 2; exerciseIndex < sheet.getLastRowNum(); exerciseIndex += 1) {
52
            ExcelExercise excelExercise =
53
                    new ExcelExercise()
54
                            .setExercise_id(getIntegerOrNull(getCellData(sheet, exerciseIndex, 0)))
55
                            .setExercise_name(getStringOrNull(getCellData(sheet, exerciseIndex, 1)))
56
                            .setUser_group_1_percent(
57
                                    getIntegerOrNull(getCellData(sheet, exerciseIndex, 3)))
58
                            .setUser_group_2_percent(
59
                                    getIntegerOrNull(getCellData(sheet, exerciseIndex, 4)))
60
                            .setUser_group_3_percent(
61
                                    getIntegerOrNull(getCellData(sheet, exerciseIndex, 5)))
62
                            .setUser_group_4_percent(
63
                                    getIntegerOrNull(getCellData(sheet, exerciseIndex, 6)))
64
                            .setBasis_for_calculations(
65
                                    getStringOrNull(getCellData(sheet, exerciseIndex, 12)));
66
            if (excelExercise.getUser_group_1_percent() != null) {
67
                excelExercises.add(excelExercise);
68
            }
69
        }
70
        return excelExercises;
71
    }
72
73
    private ExcelGoal extractGoal(
74
            int index, final Sheet sheet, List<ExcelExercise> excelExercises) {
75
        final ExcelGoal excelGoal =
76
                new ExcelGoal()
77
                        .setSheetIndex(index)
78
                        .setName(sheet.getSheetName())
79
                        .setLoops(getIntegerOrNull(getCellData(sheet, 0, 1)))
80
                        .setErrors(new ArrayList<>());
81
        String prevUserGroupName = "";
82
        String prevRoundName = "";
83
        String prevPartName = "";
84
        UserGroup userGroup = new UserGroup();
85
        Round round = new Round();
86
        Part part = new Part();
87
        for (int workoutIndex = 0;
88
                workoutIndex < sheet.getRow(0).getPhysicalNumberOfCells();
89
                workoutIndex += 1) {
90
            if (!(getCellData(sheet, 10, 2 + workoutIndex) instanceof Number)) {
91
                break;
92
            }
93
            final String userGroupName =
94
                    getNumberOrNullAsString(getCellData(sheet, 2, 2 + workoutIndex));
95
            final String roundName =
96
                    getNumberOrNullAsString(getCellData(sheet, 3, 2 + workoutIndex));
97
            final String partName = (String) getCellData(sheet, 4, 2 + workoutIndex);
98
            final boolean userGroupNameWasCreated;
99
            if (userGroupName != null && !prevUserGroupName.equals(userGroupName)) {
100
                userGroup = new UserGroup().setName(userGroupName);
101
                prevUserGroupName = userGroupName;
102
                excelGoal.getUserGroups().add(userGroup);
103
                userGroupNameWasCreated = true;
104
            } else {
105
                userGroupNameWasCreated = false;
106
            }
107
            final boolean roundNameWasCreated;
108
            if (roundName != null
109
                    && (userGroupNameWasCreated || !prevRoundName.equals(roundName))) {
110
                round = new Round().setName(roundName);
111
                prevRoundName = roundName;
112
                userGroup.getRounds().add(round);
113
                roundNameWasCreated = true;
114
            } else {
115
                roundNameWasCreated = false;
116
            }
117
            if (partName != null
118
                    && (userGroupNameWasCreated
119
                            || roundNameWasCreated
120
                            || !prevPartName.equals(partName))) {
121
                part = new Part().setName(partName);
122
                prevPartName = partName;
123
                round.getParts().add(part);
124
            }
125
            final String workoutName =
126
                    new StringJoiner("_")
127
                            .add(excelGoal.getName())
128
                            .add(userGroup.getName())
129
                            .add(round.getName())
130
                            .add(part.getName())
131
                            .toString();
132
            final Workout workout = new Workout();
133
            workout.setRowIndex(4);
134
            workout.setColumnIndex(2 + workoutIndex);
135
            workout.setName(workoutName);
136
            final Optional<WarmupWorkoutItem> warmupWorkoutItem =
137
                    extractWarmupWorkoutItem(
138
                            sheet, workoutIndex, excelGoal, workoutName, excelExercises);
139
            workout.setWarmup(warmupWorkoutItem.orElse(null));
140
            for (int workoutItemIndex = 0; workoutItemIndex < 10; workoutItemIndex += 1) {
141
                final int multiplyCoeff = 7;
142
                if (!(getCellData(sheet, 10 + workoutItemIndex * multiplyCoeff, 2 + workoutIndex)
143
                        instanceof Number)) {
144
                    break;
145
                }
146
                final Optional<WorkoutItem> workoutItem =
147
                        extractWorkoutItem(
148
                                sheet,
149
                                workoutItemIndex,
150
                                workoutIndex,
151
                                excelGoal,
152
                                workoutName,
153
                                excelExercises);
154
                if (workoutItem.isPresent()) {
155
                    workout.getWorkoutItems().add(workoutItem.get());
156
                }
157
            }
158
            part.getWorkouts().add(workout);
159
        }
160
        return excelGoal;
161
    }
162
163
    private String getOnlySymbols(String value) {
164
        return value.replace("(Weight loss)", "")
165
                .replace("Leg Press strenght", "Legpress")
166
                .replace("Chest Press", "Bench Press")
167
                .replace("Bike, Steady Pace Walk or Jog", "Bike, Steady Pace")
168
                .replaceAll("[\\s\\.\\,]+", "");
169
    }
170
171
    private Optional<WarmupWorkoutItem> extractWarmupWorkoutItem(
172
            Sheet sheet,
173
            int workoutIndex,
174
            ExcelGoal excelGoal,
175
            String workoutName,
176
            List<ExcelExercise> excelExercises) {
177
        final Optional<String> warmupName =
178
                getStringOrEmpty(getCellData(sheet, 5, 2 + workoutIndex));
179
        if (!warmupName.isPresent()) {
180
            excelGoal
181
                    .getErrors()
182
                    .add(
183
                            "Warmup name not found. Goal "
184
                                    + excelGoal.getName()
185
                                    + ", workout "
186
                                    + workoutName
187
                                    + ".");
188
            return Optional.empty();
189
        }
190
        Optional<ExcelExercise> excelExercise =
191
                excelExercises.stream()
192
                        .filter(
193
                                exercise ->
194
                                        getOnlySymbols(exercise.getExercise_name())
195
                                                .equalsIgnoreCase(getOnlySymbols(warmupName.get())))
196
                        .findFirst();
197
        if (!excelExercise.isPresent()) {
198
            excelGoal
199
                    .getErrors()
200
                    .add(
201
                            "Exercise warmup name ("
202
                                    + warmupName.get()
203
                                    + ") not recognised. Goal "
204
                                    + excelGoal.getName()
205
                                    + ", workout "
206
                                    + workoutName
207
                                    + ".");
208
        }
209
210
        Integer speedInp = extractNumbers(getCellData(sheet, 5 + 1, 2 + workoutIndex));
211
        Integer inclineInp = getIntegerOrNull(getCellData(sheet, 5 + 2, 2 + workoutIndex));
212
        Float timeInp = extractFloatNumbers(getCellData(sheet, 5 + 3, 2 + workoutIndex));
213
        return Optional.of(
214
                new WarmupWorkoutItem()
215
                        .setExercise(warmupName.get())
216
                        .setExerciseId(
217
                                excelExercise
218
                                        .orElse(new ExcelExercise().setExercise_id(0))
219
                                        .getExercise_id())
220
                        .setSpeed(speedInp)
221
                        .setIncline(inclineInp)
222
                        .setTimeInMin(timeInp));
223
    }
224
225
    private Optional<WorkoutItem> extractWorkoutItem(
226
            final Sheet sheet,
227
            int workoutItemIndex,
228
            int workoutIndex,
229
            ExcelGoal excelGoal,
230
            String workoutName,
231
            List<ExcelExercise> excelExercises) {
232
        final int multiplyCoeff = 7;
233
        WorkoutItem workoutItem = new WorkoutItem();
234
        workoutItem.setRowIndex(4 + 4 + workoutItemIndex * multiplyCoeff);
235
        workoutItem.setColumnIndex(2 + workoutIndex);
236
        final Optional<String> exerciseName =
237
                getStringOrEmpty(
238
                        getCellData(
239
                                sheet, 5 + 4 + workoutItemIndex * multiplyCoeff, 2 + workoutIndex));
240
        if (!exerciseName.isPresent()) {
241
            excelGoal
242
                    .getErrors()
243
                    .add(
244
                            "Exercise name not found. Goal "
245
                                    + excelGoal.getName()
246
                                    + ", workout "
247
                                    + workoutName
248
                                    + ".");
249
            return Optional.empty();
250
        }
251
        Optional<ExcelExercise> excelExercise =
252
                excelExercises.stream()
253
                        .filter(
254
                                exercise ->
255
                                        getOnlySymbols(exercise.getExercise_name())
256
                                                .equalsIgnoreCase(
257
                                                        getOnlySymbols(exerciseName.get())))
258
                        .findFirst();
259
        if (!excelExercise.isPresent()) {
260
            excelGoal
261
                    .getErrors()
262
                    .add(
263
                            "Exercise name ("
264
                                    + exerciseName.get()
265
                                    + ") not recognised. Goal "
266
                                    + excelGoal.getName()
267
                                    + ", workout "
268
                                    + workoutName
269
                                    + ".");
270
        } else {
271
            workoutItem.setExerciseId(excelExercise.get().getExercise_id());
272
        }
273
        Number setsInp =
274
                getNumberOrZerro(
275
                        getCellData(
276
                                sheet, 5 + 5 + workoutItemIndex * multiplyCoeff, 2 + workoutIndex));
277
        Object repetitionsInp =
278
                getStringOrNumberOrNull(
279
                        getCellData(
280
                                sheet, 5 + 6 + workoutItemIndex * multiplyCoeff, 2 + workoutIndex));
281
        Object weightInp =
282
                getStringOrNumberOrNull(
283
                        getCellData(
284
                                sheet, 5 + 7 + workoutItemIndex * multiplyCoeff, 2 + workoutIndex));
285
        workoutItem.getInput().setExercise(exerciseName.orElse(null));
286
        if (repetitionsInp instanceof String || weightInp instanceof String) {
287
            workoutItem.getInput().setSets(new ArrayList<>());
288
            for (int index = 0; index < setsInp.intValue(); index += 1) {
289
                InputSet inputSet = new InputSet();
290
                if (repetitionsInp instanceof String) {
291
                    String[] repetitionsInps = ((String) repetitionsInp).split("\\s*,\\s*");
292
                    if (exerciseName.orElse("").contains("Plank")
293
                            || repetitionsInps[index].contains("min")
294
                            || "Time"
295
                                    .equalsIgnoreCase(
296
                                            getStringOrNull(
297
                                                    getCellData(
298
                                                            sheet,
299
                                                            5
300
                                                                    + 6
301
                                                                    + workoutItemIndex
302
                                                                            * multiplyCoeff,
303
                                                            1)))) {
304
                        inputSet.setTimeInMin(
305
                                getFloatOrNull(extractFloatNumbers(repetitionsInps[index])));
306
                    } else {
307
                        inputSet.setRepetitions(
308
                                getIntegerOrNull(extractNumbers(repetitionsInps[index])));
309
                    }
310
                } else {
311
                    if ("Time"
312
                            .equalsIgnoreCase(
313
                                    getStringOrNull(
314
                                            getCellData(
315
                                                    sheet,
316
                                                    5 + 6 + workoutItemIndex * multiplyCoeff,
317
                                                    1)))) {
318
                        inputSet.setTimeInMin(getFloatOrNull(repetitionsInp));
319
                    } else {
320
                        inputSet.setRepetitions(getIntegerOrNull(repetitionsInp));
321
                    }
322
                }
323
                if (weightInp instanceof String) {
324
                    String[] weightInps = ((String) weightInp).split("\\s*,\\s*");
325
                    inputSet.setWeight(
326
                            getFloatOrNull(
327
                                    extractFloatNumbers(
328
                                            weightInps[Math.min(index, weightInps.length - 1)])));
329
                } else {
330
                    inputSet.setWeight(getFloatOrNull(weightInp));
331
                }
332
                workoutItem.getInput().getSets().add(inputSet);
333
            }
334
        } else {
335
            InputSet inputSet = new InputSet();
336
            if ("Time"
337
                    .equalsIgnoreCase(
338
                            getStringOrNull(
339
                                    getCellData(
340
                                            sheet, 5 + 6 + workoutItemIndex * multiplyCoeff, 1)))) {
341
                inputSet.setTimeInMin(getFloatOrNull(repetitionsInp));
342
            } else {
343
                inputSet.setRepetitions(getIntegerOrNull(repetitionsInp));
344
            }
345
            inputSet.setWeight(getFloatOrNull(weightInp));
346
            workoutItem.getInput().setSets(new ArrayList<>());
347
            for (int index = 0; index < setsInp.intValue(); index += 1) {
348
                workoutItem.getInput().getSets().add(inputSet);
349
            }
350
        }
351
        return Optional.of(workoutItem);
352
    }
353
354
    private Number getNumberOrZerro(Object object) {
355
        return object instanceof Number ? (Number) object : 0;
356
    }
357
358
    private String getNumberOrNullAsString(Object object) {
359
        return object instanceof Number ? String.valueOf(((Number) object).intValue()) : null;
360
    }
361
362
    private Integer extractNumbers(Object object) {
363
        if (object instanceof String) {
364
            String onlyNumbersValue = ((String) object).replaceAll("[^\\d]+", "");
365
            return onlyNumbersValue.isEmpty() ? null : Integer.parseInt(onlyNumbersValue);
366
        }
367
        return null;
368
    }
369
370
    private Float extractFloatNumbers(Object object) {
371
        if (object instanceof String) {
372
            String onlyNumbersValue = ((String) object).replaceAll("[^\\d\\.]+", "");
373
            return onlyNumbersValue.isEmpty() ? null : Float.parseFloat(onlyNumbersValue);
374
        }
375
        return null;
376
    }
377
378
    private String getStringOrNull(Object object) {
379
        return object instanceof String ? (String) object : null;
380
    }
381
382
    private Object getStringOrNumberOrNull(Object object) {
383
        if (object instanceof String || object instanceof Number) {
384
            return object;
385
        }
386
        return null;
387
    }
388
389
    private Integer getIntegerOrNull(Object object) {
390
        return object instanceof Number ? ((Number) object).intValue() : null;
391
    }
392
393
    private Float getFloatOrNull(Object object) {
394
        return object instanceof Number ? ((Number) object).floatValue() : null;
395
    }
396
397
    private Optional<String> getStringOrEmpty(Object object) {
398
        if (object instanceof String) {
399
            return Optional.of((String) object);
400
        }
401
        return Optional.empty();
402
    }
403
404
    private Object getCellData(Sheet sheet, int rowIndex, int cellIndex) {
405
        final Row row = sheet.getRow(rowIndex);
406
        if (row == null) {
407
            return null;
408
        }
409
        final Cell cell = row.getCell(cellIndex);
410
        return cell == null ? null : cellToObject(cell);
411
    }
412
413
    private Object cellToObject(Cell cell) {
414
        final CellType type = cell.getCellType();
415
        if (type == CellType.STRING) {
416
            return cleanString(cell.getStringCellValue());
417
        }
418
419
        if (type == CellType.BOOLEAN) {
420
            return cell.getBooleanCellValue();
421
        }
422
423
        if (type == CellType.NUMERIC) {
424
            if (cell.getCellStyle().getDataFormatString().contains("%")) {
425
                return cell.getNumericCellValue() * 100;
426
            }
427
            return numeric(cell);
428
        }
429
430
        if (type == CellType.FORMULA) {
431
            switch (cell.getCachedFormulaResultType()) {
0 ignored issues
show
Code Smell introduced by
Complete cases by adding the missing enum constants or add a default case to this switch.

Default branches should deal with the unexpected. At a minimum, they should log the error and if applicable, return a default value (null, empty collection). If you really do not expect the default branch to ever be use, throw a RuntimeException when it is.

Loading history...
432
                case NUMERIC:
433
                    return numeric(cell);
434
                case STRING:
435
                    return cleanString(cell.getRichStringCellValue().toString());
436
            }
437
        }
438
        return null;
439
    }
440
441
    private String cleanString(String str) {
442
        return str.replace("\n", "").replace("\r", "");
443
    }
444
445
    private Object numeric(Cell cell) {
446
        if (HSSFDateUtil.isCellDateFormatted(cell)) {
0 ignored issues
show
Obsolete introduced by
Remove this use of "HSSFDateUtil"; it is deprecated.
Loading history...
447
            return cell.getDateCellValue();
448
        }
449
        return cell.getNumericCellValue();
450
    }
451
}
452