/** * Enterprise Change Tracking Service * Demonstrates advanced Java patterns and Spring Boot integration */ @Service @RequiredArgsConstructor public class ChangeTrackingService { private final VersionChangesService versionChangesService; private final SomeListService someListService; // Thread-safe change collection private List changes; private Map referenceDataCache; /** * Main entry point - demonstrates clean API design and comprehensive change detection */ public List trackChanges( EmissionDataDto oldVersion, EmissionDataDto newVersion, boolean isRegulationAVV, boolean isRegulationEEV) { // Initialize tracking context with immutable metadata initializeTrackingContext(oldVersion, newVersion); loadReferenceData(newVersion.getReportingYear()); // Modular comparison strategy - each method handles specific data domain compareBasicData(oldVersion, newVersion); compareFuelData(oldVersion.getFuels(), newVersion.getFuels(), isRegulationEEV); compareEmissionData(oldVersion, newVersion, isRegulationAVV, isRegulationEEV); compareDocuments(oldVersion.getDocuments(), newVersion.getDocuments()); return persistChanges(); } /** * Demonstrates collection comparison with UUID-based matching */ private void compareFuelData(List oldFuels, List newFuels, boolean isSomething) { // Track removed items using functional programming approach oldFuels.stream() .filter(oldFuel -> newFuels.stream() .noneMatch(newFuel -> newFuel.getUuid().equals(oldFuel.getUuid()))) .forEach(removedFuel -> trackChange( "FUEL", "REMOVED: " + resolveFuelName(removedFuel), null, "REMOVED", "FUELS", "FUEL_MANAGEMENT" )); // Compare existing and new items with detailed field-level tracking newFuels.forEach(newFuel -> { FuelDto oldFuel = findMatchingFuel(oldFuels, newFuel.getUuid()); if (oldFuel == null) { trackNewFuel(newFuel); } else { compareFuelFields(oldFuel, newFuel, isSomething); compareMonthlyValues(oldFuel, newFuel); compareMassComponents(oldFuel.getComponents(), newFuel.getComponents()); } }); } /** * Field comparison with null-safe operations and type-specific handling */ private void compareFuelFields(FuelDto oldFuel, FuelDto newFuel, boolean isSomething) { String fuelLabel = resolveFuelName(newFuel); // BigDecimal comparison with precision handling if (!Objects.equals(oldFuel.getCalorificValue(), newFuel.getCalorificValue())) { trackChange("FUEL", fuelLabel + " - Calorific Value", oldFuel.getCalorificValue(), newFuel.getCalorificValue(), "FUELS", "FUEL_PROPERTIES"); } // Date comparison with custom formatting if (!areDatesEqual(oldFuel.getLastModified(), newFuel.getLastModified())) { trackChange("FUEL", fuelLabel + " - Last Modified", formatDate(oldFuel.getLastModified()), formatDate(newFuel.getLastModified()), "FUELS", "FUEL_METADATA"); } if (isSomething) { compareEEVSpecificFields(oldFuel, newFuel, fuelLabel); } } /** * Demonstrates intelligent reference data resolution with fallback strategies */ private String resolveFuelName(FuelDto fuel) { return Optional.ofNullable(referenceDataCache.get(fuel.getGtin())) .map(someDto::getDisplayName) .orElse(Optional.ofNullable(fuel.getCustomName()) .orElse("Unknown Fuel [" + fuel.getGtin() + "]")); } /** * Error-resilient reference data loading with graceful degradation */ private void loadReferenceData(String reportingYear) { try { this.referenceDataCache = someListService.getFuelTypes(reportingYear, false); } catch (HibernateException | DBException e) { log.warn("Reference data unavailable, using fallback strategy", e); this.referenceDataCache = new HashMap<>(); } } /** * Immutable change record creation with comprehensive metadata */ private void trackChange(String tableName, String fieldName, Object oldValue, Object newValue, String formPage, String blockName) { changes.add(VersionChange.builder() .rootRef(this.rootRef) .oldVersion(this.oldVersion) .newVersion(this.newVersion) .tableName(tableName) .fieldName(fieldName) .oldValue(formatValue(oldValue)) .newValue(formatValue(newValue)) .formPage(formPage) .blockName(blockName) .timestamp(Instant.now()) .userGln(this.userGln) .build()); } /** * Type-safe value formatting with polymorphic handling */ private String formatValue(Object value) { return switch (value) { case null -> null; case Date date -> dateFormatter.format(date); case BigDecimal decimal -> decimal.toPlainString(); case Boolean bool -> bool ? "Yes" : "No"; default -> value.toString(); }; } /** * Ppersistence with Spring transaction management */ @Transactional private List persistChanges() { if (!changes.isEmpty()) { versionChangesService.saveAll(changes); log.info("Persisted {} changes for version {}", changes.size(), newVersion); } return List.copyOf(changes); // Return immutable copy } }