commit 3bc2fdf525b1ec36ae98e6a01743ad39ca50facc Author: Kasun Jayatilaka Date: Mon Sep 8 01:38:14 2025 +0200 Upload files to "/" added demo for change tracking system diff --git a/Change-Tracking-Demo.java b/Change-Tracking-Demo.java new file mode 100644 index 0000000..6a48df4 --- /dev/null +++ b/Change-Tracking-Demo.java @@ -0,0 +1,164 @@ +/** + * 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 + } +} \ No newline at end of file