Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.

Commit cab8129

Browse files
dfcoffinclaude
andcommitted
Resolve comprehensive MapStruct mapping issues and complete missing DTO implementations
- Create missing DTO classes: RationalNumberDto and ReadingInterharmonicDto with full JAXB support - Fix MapStruct field mapping inconsistencies across all usage mappers - Enhance BaseIdentifiedObjectMapper to handle both getUuid() and getUUID() method patterns - Resolve IntervalReadingMapper and ReadingQualityMapper for non-IdentifiedObject entities - Fix ReadingTypeMapper argument vs aggregate field mapping issue - Add proper extension field handling in DateTimeIntervalMapper - Update ElectricPowerQualitySummaryMapper with systematic field mapping approach - Address inheritance pattern complications with explicit field mappings This resolves the core OpenESPI-Common-java compilation blocking issues and establishes a robust foundation for MapStruct entity-DTO conversions in the ESPI 4.0 migration. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent f099054 commit cab8129

10 files changed

+338
-115
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
*
3+
* Copyright (c) 2018-2025 Green Button Alliance, Inc.
4+
*
5+
* Portions (c) 2013-2018 EnergyOS.org
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
package org.greenbuttonalliance.espi.common.dto;
22+
23+
import jakarta.xml.bind.annotation.XmlAccessType;
24+
import jakarta.xml.bind.annotation.XmlAccessorType;
25+
import jakarta.xml.bind.annotation.XmlType;
26+
import java.math.BigInteger;
27+
28+
/**
29+
* RationalNumber DTO record for JAXB XML marshalling/unmarshalling.
30+
*
31+
* Represents a rational number with numerator and denominator components
32+
* as defined in the NAESB ESPI standard.
33+
*
34+
* @author Green Button Alliance
35+
* @version 1.4.0
36+
* @since Spring Boot 3.5
37+
*/
38+
@XmlAccessorType(XmlAccessType.FIELD)
39+
@XmlType(name = "RationalNumber", propOrder = { "numerator", "denominator" })
40+
public record RationalNumberDto(
41+
BigInteger numerator,
42+
BigInteger denominator
43+
) {
44+
/**
45+
* Default constructor for JAXB compatibility.
46+
*/
47+
public RationalNumberDto() {
48+
this(null, null);
49+
}
50+
51+
/**
52+
* Constructor with numerator only (denominator defaults to 1).
53+
*
54+
* @param numerator the numerator value
55+
*/
56+
public RationalNumberDto(BigInteger numerator) {
57+
this(numerator, BigInteger.ONE);
58+
}
59+
60+
/**
61+
* Create a RationalNumberDto from a decimal value.
62+
*
63+
* @param decimal the decimal value to convert
64+
* @param precision the number of decimal places to preserve
65+
* @return RationalNumberDto representing the decimal
66+
*/
67+
public static RationalNumberDto fromDecimal(double decimal, int precision) {
68+
if (precision < 0) {
69+
throw new IllegalArgumentException("Precision must be non-negative");
70+
}
71+
72+
BigInteger denominator = BigInteger.TEN.pow(precision);
73+
BigInteger numerator = BigInteger.valueOf((long) (decimal * Math.pow(10, precision)));
74+
75+
return new RationalNumberDto(numerator, denominator);
76+
}
77+
78+
/**
79+
* Convert this rational number to a decimal value.
80+
*
81+
* @return the decimal representation
82+
*/
83+
public double toDecimal() {
84+
if (denominator == null || denominator.equals(BigInteger.ZERO)) {
85+
return 0.0;
86+
}
87+
if (numerator == null) {
88+
return 0.0;
89+
}
90+
return numerator.doubleValue() / denominator.doubleValue();
91+
}
92+
93+
/**
94+
* Check if this rational number represents zero.
95+
*
96+
* @return true if the rational number is zero
97+
*/
98+
public boolean isZero() {
99+
return numerator == null || numerator.equals(BigInteger.ZERO);
100+
}
101+
102+
/**
103+
* Get the simplified form of this rational number.
104+
*
105+
* @return simplified RationalNumberDto
106+
*/
107+
public RationalNumberDto simplify() {
108+
if (isZero()) {
109+
return new RationalNumberDto(BigInteger.ZERO, BigInteger.ONE);
110+
}
111+
112+
if (numerator == null || denominator == null) {
113+
return this;
114+
}
115+
116+
BigInteger gcd = numerator.gcd(denominator);
117+
if (gcd.equals(BigInteger.ONE)) {
118+
return this;
119+
}
120+
121+
return new RationalNumberDto(
122+
numerator.divide(gcd),
123+
denominator.divide(gcd)
124+
);
125+
}
126+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
*
3+
* Copyright (c) 2018-2025 Green Button Alliance, Inc.
4+
*
5+
* Portions (c) 2013-2018 EnergyOS.org
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
package org.greenbuttonalliance.espi.common.dto;
22+
23+
import jakarta.xml.bind.annotation.XmlAccessType;
24+
import jakarta.xml.bind.annotation.XmlAccessorType;
25+
import jakarta.xml.bind.annotation.XmlType;
26+
import java.math.BigInteger;
27+
28+
/**
29+
* ReadingInterharmonic DTO record for JAXB XML marshalling/unmarshalling.
30+
*
31+
* Represents an interharmonic measurement with numerator and denominator components
32+
* as defined in the NAESB ESPI standard for power quality measurements.
33+
*
34+
* Interharmonics are sinusoidal components with frequencies that are not integer
35+
* multiples of the fundamental frequency. They can cause distortion in power systems.
36+
*
37+
* @author Green Button Alliance
38+
* @version 1.4.0
39+
* @since Spring Boot 3.5
40+
*/
41+
@XmlAccessorType(XmlAccessType.FIELD)
42+
@XmlType(name = "ReadingInterharmonic", propOrder = { "numerator", "denominator" })
43+
public record ReadingInterharmonicDto(
44+
BigInteger numerator,
45+
BigInteger denominator
46+
) {
47+
/**
48+
* Default constructor for JAXB compatibility.
49+
*/
50+
public ReadingInterharmonicDto() {
51+
this(null, null);
52+
}
53+
54+
/**
55+
* Constructor with numerator only (denominator defaults to 1).
56+
*
57+
* @param numerator the numerator value representing the interharmonic ratio
58+
*/
59+
public ReadingInterharmonicDto(BigInteger numerator) {
60+
this(numerator, BigInteger.ONE);
61+
}
62+
63+
/**
64+
* Create a ReadingInterharmonicDto from a frequency ratio.
65+
*
66+
* @param ratio the frequency ratio as a decimal
67+
* @param precision the number of decimal places to preserve
68+
* @return ReadingInterharmonicDto representing the frequency ratio
69+
*/
70+
public static ReadingInterharmonicDto fromRatio(double ratio, int precision) {
71+
if (precision < 0) {
72+
throw new IllegalArgumentException("Precision must be non-negative");
73+
}
74+
75+
BigInteger denominator = BigInteger.TEN.pow(precision);
76+
BigInteger numerator = BigInteger.valueOf((long) (ratio * Math.pow(10, precision)));
77+
78+
return new ReadingInterharmonicDto(numerator, denominator);
79+
}
80+
81+
/**
82+
* Convert this interharmonic to a decimal ratio.
83+
*
84+
* @return the decimal representation of the frequency ratio
85+
*/
86+
public double toRatio() {
87+
if (denominator == null || denominator.equals(BigInteger.ZERO)) {
88+
return 0.0;
89+
}
90+
if (numerator == null) {
91+
return 0.0;
92+
}
93+
return numerator.doubleValue() / denominator.doubleValue();
94+
}
95+
96+
/**
97+
* Calculate the interharmonic frequency given a fundamental frequency.
98+
*
99+
* @param fundamentalFrequency the fundamental frequency (typically 50 or 60 Hz)
100+
* @return the interharmonic frequency in Hz
101+
*/
102+
public double getFrequency(double fundamentalFrequency) {
103+
return toRatio() * fundamentalFrequency;
104+
}
105+
106+
/**
107+
* Check if this interharmonic represents zero (no interharmonic distortion).
108+
*
109+
* @return true if the interharmonic ratio is zero
110+
*/
111+
public boolean isZero() {
112+
return numerator == null || numerator.equals(BigInteger.ZERO);
113+
}
114+
115+
/**
116+
* Get the simplified form of this interharmonic ratio.
117+
*
118+
* @return simplified ReadingInterharmonicDto
119+
*/
120+
public ReadingInterharmonicDto simplify() {
121+
if (isZero()) {
122+
return new ReadingInterharmonicDto(BigInteger.ZERO, BigInteger.ONE);
123+
}
124+
125+
if (numerator == null || denominator == null) {
126+
return this;
127+
}
128+
129+
BigInteger gcd = numerator.gcd(denominator);
130+
if (gcd.equals(BigInteger.ONE)) {
131+
return this;
132+
}
133+
134+
return new ReadingInterharmonicDto(
135+
numerator.divide(gcd),
136+
denominator.divide(gcd)
137+
);
138+
}
139+
140+
/**
141+
* Check if this represents a harmonic (integer multiple of fundamental).
142+
*
143+
* @return true if this is a harmonic rather than an interharmonic
144+
*/
145+
public boolean isHarmonic() {
146+
if (isZero() || denominator == null || numerator == null) {
147+
return false;
148+
}
149+
150+
// Check if numerator is divisible by denominator (integer ratio)
151+
return numerator.remainder(denominator).equals(BigInteger.ZERO);
152+
}
153+
}

src/main/java/org/greenbuttonalliance/espi/common/dto/usage/ElectricPowerQualitySummaryDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
package org.greenbuttonalliance.espi.common.dto.usage;
2222

2323
import jakarta.xml.bind.annotation.*;
24-
import org.greenbuttonalliance.espi.common.dto.DateTimeIntervalDto;
24+
import org.greenbuttonalliance.espi.common.dto.usage.DateTimeIntervalDto;
2525

2626
/**
2727
* ElectricPowerQualitySummary DTO record for JAXB XML marshalling/unmarshalling.

src/main/java/org/greenbuttonalliance/espi/common/mapper/BaseIdentifiedObjectMapper.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,17 @@ default String entityUuidToString(Object entity) {
4949
}
5050

5151
try {
52-
// Use reflection to call getUuid() method on any IdentifiedObject entity
53-
var method = entity.getClass().getMethod("getUuid");
54-
Object result = method.invoke(entity);
55-
return result != null ? result.toString() : null;
52+
// First try getUuid() (lowercase - string field)
53+
try {
54+
var method = entity.getClass().getMethod("getUuid");
55+
Object result = method.invoke(entity);
56+
return result != null ? result.toString() : null;
57+
} catch (NoSuchMethodException e1) {
58+
// Try getUUID() (uppercase - UUID object)
59+
var method = entity.getClass().getMethod("getUUID");
60+
Object result = method.invoke(entity);
61+
return result != null ? result.toString() : null;
62+
}
5663
} catch (Exception e) {
5764
// Fallback to null if reflection fails
5865
return null;

src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/DateTimeIntervalMapper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ public interface DateTimeIntervalMapper {
5454
*/
5555
@Mapping(target = "start", source = "start")
5656
@Mapping(target = "duration", source = "duration")
57+
@Mapping(target = "extension", ignore = true) // Extension field not present in DTO
5758
DateTimeInterval toEntity(DateTimeIntervalDto dto);
5859
}

src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/ElectricPowerQualitySummaryMapper.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,15 @@ public interface ElectricPowerQualitySummaryMapper extends BaseIdentifiedObjectM
5858
*/
5959
@Mapping(target = "id", ignore = true)
6060
@Mapping(target = "uuid", ignore = true) // UUID is computed from hashedId
61-
@Mapping(target = "published", ignore = true)
61+
@Mapping(target = "uuidMostSignificantBits", ignore = true)
62+
@Mapping(target = "uuidLeastSignificantBits", ignore = true)
63+
@Mapping(target = "created", ignore = true)
6264
@Mapping(target = "updated", ignore = true)
63-
@Mapping(target = "usagePoint", ignore = true) // Relationships handled separately
64-
@Mapping(target = "relatedLinks", ignore = true)
65-
@Mapping(target = "selfLink", ignore = true)
65+
@Mapping(target = "published", ignore = true)
6666
@Mapping(target = "upLink", ignore = true)
67+
@Mapping(target = "selfLink", ignore = true)
68+
@Mapping(target = "relatedLinks", ignore = true)
69+
@Mapping(target = "usagePoint", ignore = true) // Relationships handled separately
6770
ElectricPowerQualitySummaryEntity toEntity(ElectricPowerQualitySummaryDto dto);
6871

6972
/**
@@ -75,11 +78,14 @@ public interface ElectricPowerQualitySummaryMapper extends BaseIdentifiedObjectM
7578
*/
7679
@Mapping(target = "id", ignore = true)
7780
@Mapping(target = "uuid", ignore = true) // UUID is computed from hashedId
78-
@Mapping(target = "published", ignore = true)
81+
@Mapping(target = "uuidMostSignificantBits", ignore = true)
82+
@Mapping(target = "uuidLeastSignificantBits", ignore = true)
83+
@Mapping(target = "created", ignore = true)
7984
@Mapping(target = "updated", ignore = true)
80-
@Mapping(target = "usagePoint", ignore = true)
81-
@Mapping(target = "relatedLinks", ignore = true)
82-
@Mapping(target = "selfLink", ignore = true)
85+
@Mapping(target = "published", ignore = true)
8386
@Mapping(target = "upLink", ignore = true)
87+
@Mapping(target = "selfLink", ignore = true)
88+
@Mapping(target = "relatedLinks", ignore = true)
89+
@Mapping(target = "usagePoint", ignore = true)
8490
void updateEntity(ElectricPowerQualitySummaryDto dto, @MappingTarget ElectricPowerQualitySummaryEntity entity);
8591
}

0 commit comments

Comments
 (0)