From acf3b458565bb6b37e464c8cdbc28c018dee1a28 Mon Sep 17 00:00:00 2001 From: Bryson Spilman Date: Fri, 2 Oct 2026 13:34:29 -0700 Subject: [PATCH] CDA-138 - Adds Element DTOs for rating components and updating ratings json util to use jackson --- .../cwms/cda/api/rating/RatingController.java | 49 ++- .../api/rating/RatingLatestController.java | 4 +- .../api/rating/RatingTemplateController.java | 12 +- .../cwms/cda/data/dao/JsonRatingUtils.java | 200 +++++------ .../data/dto/rating/document/CaseElement.java | 105 ++++++ .../rating/document/HeightOffsetsElement.java | 69 ++++ .../rating/document/HeightShiftsElement.java | 153 +++++++++ .../document/IndParameterSpecElement.java | 136 ++++++++ .../document/IndParameterSpecsElement.java | 69 ++++ .../document/IndRoundingSpecsElement.java | 69 ++++ .../data/dto/rating/document/ListUtil.java | 48 +++ .../dto/rating/document/OtherIndElement.java | 91 +++++ .../dto/rating/document/PointElement.java | 108 ++++++ .../document/PositionalValueElement.java | 96 ++++++ .../rating/document/RatingPointsElement.java | 88 +++++ .../rating/document/RatingSpecElement.java | 318 ++++++++++++++++++ .../document/RatingTemplateElement.java | 145 ++++++++ .../dto/rating/document/RatingsDocument.java | 231 +++++++++++++ .../dto/rating/document/SelectElement.java | 86 +++++ .../rating/document/SimpleRatingElement.java | 248 ++++++++++++++ .../rating/document/SourceRatingElement.java | 105 ++++++ .../document/TransitionalRatingElement.java | 211 ++++++++++++ .../TransitionalSourceRatingsElement.java | 69 ++++ .../document/UsgsStreamRatingElement.java | 265 +++++++++++++++ .../document/VerticalDatumInfoElement.java | 165 +++++++++ .../document/VerticalDatumOffsetElement.java | 105 ++++++ .../rating/document/VirtualRatingElement.java | 196 +++++++++++ .../document/VirtualSourceRatingsElement.java | 69 ++++ .../cda/data/dao/JsonRatingUtilsTest.java | 223 +++++++++++- 29 files changed, 3576 insertions(+), 157 deletions(-) create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/CaseElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightOffsetsElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightShiftsElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecsElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndRoundingSpecsElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/ListUtil.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/OtherIndElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PointElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PositionalValueElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingPointsElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingSpecElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingTemplateElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingsDocument.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SelectElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SimpleRatingElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SourceRatingElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalRatingElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalSourceRatingsElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/UsgsStreamRatingElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumInfoElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumOffsetElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualRatingElement.java create mode 100644 cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualSourceRatingsElement.java diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java index 9225df4c8a..0ba1e3e887 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingController.java @@ -54,6 +54,7 @@ import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.google.common.flogger.FluentLogger; import cwms.cda.api.BaseCrudHandler; @@ -68,6 +69,7 @@ import cwms.cda.data.dto.StatusResponse; import cwms.cda.data.dto.VerticalDatumInfo; import cwms.cda.data.dto.rating.Ratings; +import cwms.cda.data.dto.rating.document.RatingsDocument; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; import cwms.cda.formatters.annotations.FormattableWith; @@ -92,7 +94,6 @@ import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; -import javax.xml.transform.TransformerException; import mil.army.usace.hec.cwms.rating.io.xml.RatingXmlFactory; import mil.army.usace.hec.metadata.VerticalDatumException; import org.jetbrains.annotations.NotNull; @@ -157,7 +158,8 @@ public void create(@NotNull Context ctx) { DSLContext dsl = getDslContext(ctx); RatingDao ratingDao = getRatingDao(dsl); boolean storeTemplate = ctx.queryParamAsClass(STORE_TEMPLATE, Boolean.class).getOrDefault(true); - String ratingSet = deserializeRatingSet(ctx, storeTemplate); + RatingsDocument ratings = deserializeRatingSet(ctx, storeTemplate); + String ratingSet = JsonRatingUtils.writeXml(ratings); String datum = ctx.queryParam(DATUM); VerticalDatum vd = null; if(datum != null) { @@ -181,7 +183,7 @@ public void create(@NotNull Context ctx) { } } - private String deserializeRatingSet(Context ctx, boolean storeTemplate) throws IOException, RatingException { + private RatingsDocument deserializeRatingSet(Context ctx, boolean storeTemplate) throws IOException { String formatHeader = ctx.req.getContentType(); //Using placeholder CwmsDTOBase.class since we do not have a RatingSet DTO //The contentType will match against the standard listing of Formats constants @@ -191,33 +193,29 @@ private String deserializeRatingSet(Context ctx, boolean storeTemplate) throws I } //Package private for unit testing - String deserializeRatingSet(String body, String contentType, boolean storeTemplate) throws IOException { - String retval; - if (Formats.XML.equals(contentType)) { - retval = body; - } else if (Formats.JSON.equals(contentType)) { - retval = translateJsonToXml(body); - } else { - throw new IOException("Unexpected format:" + contentType); + RatingsDocument deserializeRatingSet(String body, String contentType, boolean storeTemplate) throws IOException { + RatingsDocument retval; + try { + if (Formats.XML.equals(contentType)) { + retval = JsonRatingUtils.readXml(body); + } else if (Formats.JSON.equals(contentType)) { + retval = JsonRatingUtils.readJson(body); + } else { + throw new IOException("Unexpected format:" + contentType); + } + } catch (JsonProcessingException ex) { + throw new IllegalArgumentException("Failed to parse request into a rating set", ex); } if (!storeTemplate) { - retval = removeTemplate(retval); - } - return retval; - } - - private static String translateJsonToXml(String body) { - String retval; - try { - retval = JsonRatingUtils.jsonToXml(body); - } catch (IOException | TransformerException ex) { - throw new IllegalArgumentException("Failed to translate request into rating spec XML", ex); + retval = removeTemplates(retval); } return retval; } - private String removeTemplate(String xml) { - return xml.replaceAll("(?s)", ""); + private static RatingsDocument removeTemplates(RatingsDocument ratings) { + return new RatingsDocument.Builder(ratings) + .withRatingTemplates(null) + .build(); } @OpenApi( @@ -588,7 +586,8 @@ public void update(@NotNull Context ctx, @NotNull String ratingId) { .getOrDefault(true); boolean replaceBaseCurve = ctx.queryParamAsClass(REPLACE_BASE_CURVE, Boolean.class) .getOrDefault(false); - String ratingSet = deserializeRatingSet(ctx, storeTemplate); + RatingsDocument ratings = deserializeRatingSet(ctx, storeTemplate); + String ratingSet = JsonRatingUtils.writeXml(ratings); String datum = ctx.queryParam(DATUM); VerticalDatum vd = null; if(datum != null) { diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingLatestController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingLatestController.java index 70b4827d9b..d1eb4e838d 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingLatestController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingLatestController.java @@ -39,6 +39,7 @@ import cwms.cda.api.errors.CdaError; import cwms.cda.api.errors.ExceptionTraceSupport; import cwms.cda.data.dao.JsonRatingUtils; +import cwms.cda.data.dto.rating.document.RatingsDocument; import cwms.cda.data.dao.RatingDao; import cwms.cda.data.dao.RatingSetDao; import cwms.cda.formatters.ContentType; @@ -134,7 +135,8 @@ private String getLatestRatingSet(Context ctx, String officeId, String rating, C ratingSet = ratingDao.retrieveLatestXML(officeId, rating); if (contentType.toString().equals(Formats.JSONV2)) { - ratingSet = JsonRatingUtils.xmlToJson(ratingSet); + RatingsDocument ratings = JsonRatingUtils.readXml(ratingSet); + ratingSet = JsonRatingUtils.writeJson(ratings); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java index 98dee2dfa0..d0386e4fb3 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/rating/RatingTemplateController.java @@ -31,6 +31,7 @@ import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; +import com.fasterxml.jackson.core.JsonProcessingException; import cwms.cda.api.Controllers; import cwms.cda.api.errors.CdaError; import cwms.cda.data.dao.JooqDao; @@ -38,6 +39,7 @@ import cwms.cda.data.dao.RatingTemplateDao; import cwms.cda.data.dto.rating.RatingTemplate; import cwms.cda.data.dto.rating.RatingTemplates; +import cwms.cda.data.dto.rating.document.RatingsDocument; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; import io.javalin.apibuilder.CrudHandler; @@ -53,7 +55,6 @@ import java.util.Optional; import com.google.common.flogger.FluentLogger; import javax.servlet.http.HttpServletResponse; -import javax.xml.transform.TransformerException; import org.jetbrains.annotations.NotNull; import org.jooq.DSLContext; @@ -255,13 +256,12 @@ private static String translateToXml(String body, String contentType) { } private static String translateJsonToXml(String body) { - String retval; try { - retval = JsonRatingUtils.jsonToXml(body); - } catch (IOException | TransformerException ex) { - throw new IllegalArgumentException("Failed to translate request into rating spec XML", ex); + RatingsDocument ratings = JsonRatingUtils.readJson(body); + return JsonRatingUtils.writeXml(ratings); + } catch (JsonProcessingException ex) { + throw new IllegalArgumentException("Failed to translate request into rating template XML", ex); } - return retval; } @OpenApi(ignore = true) diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/JsonRatingUtils.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/JsonRatingUtils.java index 8996891c01..7ac3d788ca 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/JsonRatingUtils.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/JsonRatingUtils.java @@ -1,48 +1,61 @@ package cwms.cda.data.dao; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.cfg.CoercionAction; +import com.fasterxml.jackson.databind.cfg.CoercionInputShape; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.fasterxml.jackson.databind.type.LogicalType; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import cwms.cda.data.dto.rating.document.RatingsDocument; import hec.data.RatingException; import hec.data.cwmsRating.RatingSet; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringReader; -import java.io.StringWriter; -import java.util.stream.Collectors; -import javax.xml.XMLConstants; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import mil.army.usace.hec.cwms.rating.io.xml.RatingXmlFactory; -public class JsonRatingUtils { + +public final class JsonRatingUtils { + + private static final ObjectMapper JSON_MAPPER = JsonMapper.builder() + // Repeated xml elements that only occur once are written (and may be sent back) + // as a plain object instead of a one element array. + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .enable(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) + // An empty element such as is "" in json, read it back as an empty object. + .withCoercionConfig(LogicalType.POJO, + cfg -> cfg.setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty)) + .build(); + + private static final XmlMapper XML_MAPPER = XmlMapper.builder() + .defaultUseWrapper(false) + // Lets an aliased or stray single element land in a List property. + .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + .build(); + private JsonRatingUtils() { } public static RatingSet fromJson(String json) throws RatingException { - String xml; try { - xml = jsonToXml(json); - } catch (IOException | TransformerException e) { + return toRatingSet(readJson(json)); + } catch (JsonProcessingException e) { throw new RatingException(e); } - - return RatingXmlFactory.ratingSet(xml); } public static String toJson(RatingSet ratingSet) throws RatingException { String retval = null; if (ratingSet != null) { try { - retval = xmlToJson(RatingXmlFactory.toXml(ratingSet, " ")); + retval = writeJson(fromRatingSet(ratingSet)); } catch (JsonProcessingException e) { throw new RatingException(e); } @@ -50,113 +63,72 @@ public static String toJson(RatingSet ratingSet) throws RatingException { return retval; } - public static String jsonToXml(String json) throws IOException, TransformerException { - ObjectMapper om = new ObjectMapper(); - - JsonNode jsonNode = om.readTree(json); - - XmlMapper mapper = new XmlMapper(); - ObjectWriter writer = mapper.writer() - .withRootName("ratings"); - String xml = writer.writeValueAsString(jsonNode); - - return cleanupXml(xml); + public static RatingsDocument fromRatingSet(RatingSet ratingSet) + throws JsonProcessingException { + return readXml(RatingXmlFactory.toXml(ratingSet, " ")); } - private static String cleanupXml(String xml) throws TransformerException { - // Doing this in steps b/c I'm not good enough at xslt to make it happen at once. - - // The way we are writing out json, all the xml attributes were turned into - // child json fields. We know certain fields (e.g. office-id, position) - // should be attributes. - String resourceLocation = "/cwms/cda/data/rating/remove_office.xsl"; - InputStream resourceAsStream = JsonRatingUtils.class.getResourceAsStream(resourceLocation); - String officeXsl = readStream(resourceAsStream); - - xml = applyTransform(xml, new StreamSource(new StringReader(officeXsl))); - - - String[] additionalAttributes = new String[]{"position", "estimate", "unit",}; - - for (String attributeName : additionalAttributes) { - String template = officeXsl.replace("office-id", attributeName); - xml = applyTransform(xml, new StreamSource(new StringReader(template))); - } - - // Value should become an attribute except when its inside offset so it needs - // a special transform. - xml = applyTransform(xml, buildSourceFromResource("move_value.xsl")); + public static RatingSet toRatingSet(RatingsDocument ratings) + throws RatingException, JsonProcessingException { + return RatingXmlFactory.ratingSet(writeXml(ratings)); + } - // There is also the issue where the value of some elements was being - // written as an empty child field. We manually renamed - // those to element-value in the json transformation. - // move_element-value will move the value of element-value - // back into the value of the parent element. - xml = applyTransform(xml, buildSourceFromResource("move_element-value.xsl")); + public static RatingsDocument readJson(String json) throws JsonProcessingException { + return JSON_MAPPER.readValue(json, RatingsDocument.class); + } - return xml; + public static String writeJson(RatingsDocument ratings) throws JsonProcessingException { + JsonNode tree = JSON_MAPPER.valueToTree(ratings); + replaceEmptyObjectsWithEmptyStrings(tree); + return JSON_MAPPER.writeValueAsString(tree); } - static Source buildSourceFromResource(String filename) { - String resourceLocation = "/cwms/cda/data/rating/" + filename; - InputStream resourceAsStream = JsonRatingUtils.class.getResourceAsStream(resourceLocation); - if (resourceAsStream == null) { - throw new IllegalArgumentException("Could not find resource: " + resourceLocation); + // The legacy json was a direct conversion of the xml tree, where an empty element such as + // became "" rather than {}. + private static void replaceEmptyObjectsWithEmptyStrings(JsonNode node) { + if (node instanceof ObjectNode) { + ObjectNode object = (ObjectNode) node; + List emptyFields = new ArrayList<>(); + for (Map.Entry field : object.properties()) { + if (isEmptyObject(field.getValue())) { + emptyFields.add(field.getKey()); + } else { + replaceEmptyObjectsWithEmptyStrings(field.getValue()); + } + } + emptyFields.forEach(name -> object.put(name, "")); + } else if (node instanceof ArrayNode) { + ArrayNode array = (ArrayNode) node; + for (int i = 0; i < array.size(); i++) { + if (isEmptyObject(array.get(i))) { + array.set(i, TextNode.valueOf("")); + } else { + replaceEmptyObjectsWithEmptyStrings(array.get(i)); + } + } } - - return new StreamSource(resourceAsStream); } - private static String readStream(InputStream inputStream) { - return new BufferedReader(new InputStreamReader(inputStream)) - .lines().collect(Collectors.joining("\n")); + private static boolean isEmptyObject(JsonNode node) { + return node.isObject() && node.isEmpty(); } - static String applyTransform(String xml, Source xslt) throws TransformerException { - TransformerFactory factory = TransformerFactory.newInstance(); - factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); - factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); - - Transformer transformer = factory.newTransformer(xslt); - - StringReader input = new StringReader(xml); - StreamSource source = new StreamSource(input); + public static RatingsDocument readXml(String xml) throws JsonProcessingException { + return XML_MAPPER.readValue(xml, RatingsDocument.class); + } - StringWriter sw = new StringWriter(); - StreamResult outputResult = new StreamResult(sw); - transformer.transform(source, outputResult); + public static String writeXml(RatingsDocument ratings) throws JsonProcessingException { + RatingsDocument dbRatings = new RatingsDocument.Builder(ratings) + .withNoNamespaceSchemaLocation(null) + .build(); + return XML_MAPPER.writeValueAsString(dbRatings); + } - return sw.toString(); + public static String jsonToXml(String json) throws JsonProcessingException { + return writeXml(readJson(json)); } public static String xmlToJson(String xml) throws JsonProcessingException { - XmlMapper mapper = new XmlMapper(); - JsonNode jsonNode = mapper.readTree(xml); - - ObjectMapper om = new ObjectMapper(); - ObjectWriter writer = om.writer(); - - String json = writer.writeValueAsString(jsonNode); - - // When converted to json by parsing with XmlMapper - // and passing the result to ObjectMapper - // the xml like: - // - // 2223456782 - // - // becomes: - // "ind-rounding-specs": { - // "ind-rounding-spec": { - // "position": "1", - // "": "2223456782" - // } - // } - // - // There is a weird field with an empty name in the json... - // Lets find those and rename the empty field to something else. - - json = json.replace("\"\":", "\"element-value\":"); - - return json; + return writeJson(readXml(xml)); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/CaseElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/CaseElement.java new file mode 100644 index 0000000000..db744b15a0 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/CaseElement.java @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = CaseElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"position", "when", "then"}) +public final class CaseElement { + + @JsonProperty("position") + @JacksonXmlProperty(isAttribute = true, localName = "position") + private final String position; + + @JsonProperty("when") + private final String when; + + @JsonProperty("then") + private final String then; + + private CaseElement(Builder builder) { + this.position = builder.position; + this.when = builder.when; + this.then = builder.then; + } + + public String getPosition() { + return position; + } + + public String getWhen() { + return when; + } + + public String getThen() { + return then; + } + + @JsonPOJOBuilder + public static final class Builder { + private String position; + private String when; + private String then; + + @JsonProperty("position") + public Builder withPosition(String position) { + this.position = position; + return this; + } + + @JsonIgnore + public Builder withPosition(int position) { + return withPosition(String.valueOf(position)); + } + + @JsonProperty("when") + public Builder withWhen(String when) { + this.when = when; + return this; + } + + @JsonProperty("then") + public Builder withThen(String then) { + this.then = then; + return this; + } + + public CaseElement build() { + return new CaseElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightOffsetsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightOffsetsElement.java new file mode 100644 index 0000000000..a60cbcd6b8 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightOffsetsElement.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = HeightOffsetsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class HeightOffsetsElement { + + @JsonProperty("point") + @JacksonXmlElementWrapper(useWrapping = false) + private final List points; + + private HeightOffsetsElement(Builder builder) { + this.points = ListUtil.copyOf(builder.points); + } + + public List getPoints() { + return points; + } + + @JsonPOJOBuilder + public static final class Builder { + private List points; + + @JsonProperty("point") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withPoints(List points) { + this.points = points; + return this; + } + + public HeightOffsetsElement build() { + return new HeightOffsetsElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightShiftsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightShiftsElement.java new file mode 100644 index 0000000000..f67d2d2a5f --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/HeightShiftsElement.java @@ -0,0 +1,153 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = HeightShiftsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"effective-date", "transition-start-date", "create-date", "active", + "description", "point"}) +public final class HeightShiftsElement { + + @JsonProperty("effective-date") + private final String effectiveDate; + + @JsonProperty("transition-start-date") + private final String transitionStartDate; + + @JsonProperty("create-date") + private final String createDate; + + @JsonProperty("active") + private final String active; + + @JsonProperty("description") + private final String description; + + @JsonProperty("point") + @JacksonXmlElementWrapper(useWrapping = false) + private final List points; + + private HeightShiftsElement(Builder builder) { + this.effectiveDate = builder.effectiveDate; + this.transitionStartDate = builder.transitionStartDate; + this.createDate = builder.createDate; + this.active = builder.active; + this.description = builder.description; + this.points = ListUtil.copyOf(builder.points); + } + + public String getEffectiveDate() { + return effectiveDate; + } + + public String getTransitionStartDate() { + return transitionStartDate; + } + + public String getCreateDate() { + return createDate; + } + + public String getActive() { + return active; + } + + public String getDescription() { + return description; + } + + public List getPoints() { + return points; + } + + @JsonPOJOBuilder + public static final class Builder { + private String effectiveDate; + private String transitionStartDate; + private String createDate; + private String active; + private String description; + private List points; + + @JsonProperty("effective-date") + public Builder withEffectiveDate(String effectiveDate) { + this.effectiveDate = effectiveDate; + return this; + } + + @JsonProperty("transition-start-date") + public Builder withTransitionStartDate(String transitionStartDate) { + this.transitionStartDate = transitionStartDate; + return this; + } + + @JsonProperty("create-date") + public Builder withCreateDate(String createDate) { + this.createDate = createDate; + return this; + } + + @JsonProperty("active") + public Builder withActive(String active) { + this.active = active; + return this; + } + + @JsonIgnore + public Builder withActive(boolean active) { + return withActive(String.valueOf(active)); + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + @JsonProperty("point") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withPoints(List points) { + this.points = points; + return this; + } + + public HeightShiftsElement build() { + return new HeightShiftsElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecElement.java new file mode 100644 index 0000000000..d85f959dcf --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecElement.java @@ -0,0 +1,136 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = IndParameterSpecElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"position", "parameter", "in-range-method", "out-range-low-method", + "out-range-high-method"}) +public final class IndParameterSpecElement { + + @JsonProperty("position") + @JacksonXmlProperty(isAttribute = true, localName = "position") + private final String position; + + @JsonProperty("parameter") + private final String parameter; + + @JsonProperty("in-range-method") + private final String inRangeMethod; + + @JsonProperty("out-range-low-method") + private final String outRangeLowMethod; + + @JsonProperty("out-range-high-method") + private final String outRangeHighMethod; + + private IndParameterSpecElement(Builder builder) { + this.position = builder.position; + this.parameter = builder.parameter; + this.inRangeMethod = builder.inRangeMethod; + this.outRangeLowMethod = builder.outRangeLowMethod; + this.outRangeHighMethod = builder.outRangeHighMethod; + } + + public String getPosition() { + return position; + } + + public String getParameter() { + return parameter; + } + + public String getInRangeMethod() { + return inRangeMethod; + } + + public String getOutRangeLowMethod() { + return outRangeLowMethod; + } + + public String getOutRangeHighMethod() { + return outRangeHighMethod; + } + + @JsonPOJOBuilder + public static final class Builder { + private String position; + private String parameter; + private String inRangeMethod; + private String outRangeLowMethod; + private String outRangeHighMethod; + + @JsonProperty("position") + public Builder withPosition(String position) { + this.position = position; + return this; + } + + @JsonIgnore + public Builder withPosition(int position) { + return withPosition(String.valueOf(position)); + } + + @JsonProperty("parameter") + public Builder withParameter(String parameter) { + this.parameter = parameter; + return this; + } + + @JsonProperty("in-range-method") + public Builder withInRangeMethod(String inRangeMethod) { + this.inRangeMethod = inRangeMethod; + return this; + } + + @JsonProperty("out-range-low-method") + public Builder withOutRangeLowMethod(String outRangeLowMethod) { + this.outRangeLowMethod = outRangeLowMethod; + return this; + } + + @JsonProperty("out-range-high-method") + public Builder withOutRangeHighMethod(String outRangeHighMethod) { + this.outRangeHighMethod = outRangeHighMethod; + return this; + } + + public IndParameterSpecElement build() { + return new IndParameterSpecElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecsElement.java new file mode 100644 index 0000000000..908843a9bd --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndParameterSpecsElement.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = IndParameterSpecsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class IndParameterSpecsElement { + + @JsonProperty("ind-parameter-spec") + @JacksonXmlElementWrapper(useWrapping = false) + private final List indParameterSpecs; + + private IndParameterSpecsElement(Builder builder) { + this.indParameterSpecs = ListUtil.copyOf(builder.indParameterSpecs); + } + + public List getIndParameterSpecs() { + return indParameterSpecs; + } + + @JsonPOJOBuilder + public static final class Builder { + private List indParameterSpecs; + + @JsonProperty("ind-parameter-spec") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withIndParameterSpecs(List indParameterSpecs) { + this.indParameterSpecs = indParameterSpecs; + return this; + } + + public IndParameterSpecsElement build() { + return new IndParameterSpecsElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndRoundingSpecsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndRoundingSpecsElement.java new file mode 100644 index 0000000000..a07b87378c --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/IndRoundingSpecsElement.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = IndRoundingSpecsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class IndRoundingSpecsElement { + + @JsonProperty("ind-rounding-spec") + @JacksonXmlElementWrapper(useWrapping = false) + private final List indRoundingSpecs; + + private IndRoundingSpecsElement(Builder builder) { + this.indRoundingSpecs = ListUtil.copyOf(builder.indRoundingSpecs); + } + + public List getIndRoundingSpecs() { + return indRoundingSpecs; + } + + @JsonPOJOBuilder + public static final class Builder { + private List indRoundingSpecs; + + @JsonProperty("ind-rounding-spec") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withIndRoundingSpecs(List indRoundingSpecs) { + this.indRoundingSpecs = indRoundingSpecs; + return this; + } + + public IndRoundingSpecsElement build() { + return new IndRoundingSpecsElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/ListUtil.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/ListUtil.java new file mode 100644 index 0000000000..807340e1b2 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/ListUtil.java @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import java.util.ArrayList; +import java.util.List; + +final class ListUtil { + + private ListUtil() { + throw new AssertionError("Utility class"); + } + + static List copyOf(List source) { + return source == null ? null : new ArrayList<>(source); + } + + static List append(List target, List values) { + if (values == null) { + return target; + } + List retval = target == null ? new ArrayList<>() : target; + retval.addAll(values); + return retval; + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/OtherIndElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/OtherIndElement.java new file mode 100644 index 0000000000..92c1cb5c65 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/OtherIndElement.java @@ -0,0 +1,91 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = OtherIndElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"position", "value"}) +public final class OtherIndElement { + + @JsonProperty("position") + @JacksonXmlProperty(isAttribute = true, localName = "position") + private final String position; + + @JsonProperty("value") + @JacksonXmlProperty(isAttribute = true, localName = "value") + private final String value; + + private OtherIndElement(Builder builder) { + this.position = builder.position; + this.value = builder.value; + } + + public String getPosition() { + return position; + } + + public String getValue() { + return value; + } + + @JsonPOJOBuilder + public static final class Builder { + private String position; + private String value; + + @JsonProperty("position") + public Builder withPosition(String position) { + this.position = position; + return this; + } + + @JsonIgnore + public Builder withPosition(int position) { + return withPosition(String.valueOf(position)); + } + + @JsonProperty("value") + public Builder withValue(String value) { + this.value = value; + return this; + } + + public OtherIndElement build() { + return new OtherIndElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PointElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PointElement.java new file mode 100644 index 0000000000..b4bad25ef3 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PointElement.java @@ -0,0 +1,108 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; + +@JsonDeserialize(builder = PointElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"ind", "dep", "note"}) +public final class PointElement { + + @JsonProperty("ind") + private final String ind; + + @JsonProperty("dep") + private final String dep; + + @JsonProperty("note") + private final String note; + + private PointElement(Builder builder) { + this.ind = builder.ind; + this.dep = builder.dep; + this.note = builder.note; + } + + public String getInd() { + return ind; + } + + public String getDep() { + return dep; + } + + public String getNote() { + return note; + } + + @JsonPOJOBuilder + public static final class Builder { + private String ind; + private String dep; + private String note; + + @JsonProperty("ind") + public Builder withInd(String ind) { + this.ind = ind; + return this; + } + + @JsonIgnore + public Builder withInd(double ind) { + return withInd(String.valueOf(ind)); + } + + @JsonProperty("dep") + public Builder withDep(String dep) { + this.dep = dep; + return this; + } + + @JsonIgnore + public Builder withDep(double dep) { + return withDep(String.valueOf(dep)); + } + + @JsonProperty("note") + public Builder withNote(String note) { + this.note = note; + return this; + } + + public PointElement build() { + return new PointElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PositionalValueElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PositionalValueElement.java new file mode 100644 index 0000000000..1fed1f46aa --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/PositionalValueElement.java @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText; + +@JsonDeserialize(builder = PositionalValueElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({PositionalValueElement.POSITION, PositionalValueElement.ELEMENT_VALUE}) +public final class PositionalValueElement { + + static final String POSITION = "position"; + static final String ELEMENT_VALUE = "element-value"; + + @JsonProperty(POSITION) + @JacksonXmlProperty(isAttribute = true, localName = POSITION) + private final String position; + + @JsonProperty(ELEMENT_VALUE) + @JacksonXmlText + private final String value; + + private PositionalValueElement(Builder builder) { + this.position = builder.position; + this.value = builder.value; + } + + public String getPosition() { + return position; + } + + public String getValue() { + return value; + } + + @JsonPOJOBuilder + public static final class Builder { + private String position; + private String value; + + @JsonProperty(POSITION) + public Builder withPosition(String position) { + this.position = position; + return this; + } + + @JsonIgnore + public Builder withPosition(int position) { + return withPosition(String.valueOf(position)); + } + + @JsonProperty(ELEMENT_VALUE) + @JacksonXmlText + public Builder withValue(String value) { + this.value = value; + return this; + } + + public PositionalValueElement build() { + return new PositionalValueElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingPointsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingPointsElement.java new file mode 100644 index 0000000000..448cbabe7b --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingPointsElement.java @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = RatingPointsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"other-ind", "point"}) +public final class RatingPointsElement { + + @JsonProperty("other-ind") + @JacksonXmlElementWrapper(useWrapping = false) + private final List otherInds; + + @JsonProperty("point") + @JacksonXmlElementWrapper(useWrapping = false) + private final List points; + + private RatingPointsElement(Builder builder) { + this.otherInds = ListUtil.copyOf(builder.otherInds); + this.points = ListUtil.copyOf(builder.points); + } + + public List getOtherInds() { + return otherInds; + } + + public List getPoints() { + return points; + } + + @JsonPOJOBuilder + public static final class Builder { + private List otherInds; + private List points; + + @JsonProperty("other-ind") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withOtherInds(List otherInds) { + this.otherInds = otherInds; + return this; + } + + @JsonProperty("point") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withPoints(List points) { + this.points = points; + return this; + } + + public RatingPointsElement build() { + return new RatingPointsElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingSpecElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingSpecElement.java new file mode 100644 index 0000000000..489ba78059 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingSpecElement.java @@ -0,0 +1,318 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = RatingSpecElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office-id", "rating-spec-id", "template-id", "location-id", "version", + "source-agency", "in-range-method", "out-range-low-method", "out-range-high-method", "active", + "auto-update", "auto-activate", "auto-migrate-extension", "ind-rounding-specs", + "dep-rounding-spec", "description"}) +public final class RatingSpecElement { + + @JsonProperty("office-id") + @JacksonXmlProperty(isAttribute = true, localName = "office-id") + private final String officeId; + + @JsonProperty("rating-spec-id") + private final String ratingSpecId; + + @JsonProperty("template-id") + private final String templateId; + + @JsonProperty("location-id") + private final String locationId; + + @JsonProperty("version") + private final String version; + + @JsonProperty("source-agency") + private final String sourceAgency; + + @JsonProperty("in-range-method") + private final String inRangeMethod; + + @JsonProperty("out-range-low-method") + private final String outRangeLowMethod; + + @JsonProperty("out-range-high-method") + private final String outRangeHighMethod; + + @JsonProperty("active") + private final String active; + + @JsonProperty("auto-update") + private final String autoUpdate; + + @JsonProperty("auto-activate") + private final String autoActivate; + + @JsonProperty("auto-migrate-extension") + private final String autoMigrateExtension; + + @JsonProperty("ind-rounding-specs") + private final IndRoundingSpecsElement indRoundingSpecs; + + @JsonProperty("dep-rounding-spec") + private final String depRoundingSpec; + + @JsonProperty("description") + private final String description; + + private RatingSpecElement(Builder builder) { + this.officeId = builder.officeId; + this.ratingSpecId = builder.ratingSpecId; + this.templateId = builder.templateId; + this.locationId = builder.locationId; + this.version = builder.version; + this.sourceAgency = builder.sourceAgency; + this.inRangeMethod = builder.inRangeMethod; + this.outRangeLowMethod = builder.outRangeLowMethod; + this.outRangeHighMethod = builder.outRangeHighMethod; + this.active = builder.active; + this.autoUpdate = builder.autoUpdate; + this.autoActivate = builder.autoActivate; + this.autoMigrateExtension = builder.autoMigrateExtension; + this.indRoundingSpecs = builder.indRoundingSpecs; + this.depRoundingSpec = builder.depRoundingSpec; + this.description = builder.description; + } + + public String getOfficeId() { + return officeId; + } + + public String getRatingSpecId() { + return ratingSpecId; + } + + public String getTemplateId() { + return templateId; + } + + public String getLocationId() { + return locationId; + } + + public String getVersion() { + return version; + } + + public String getSourceAgency() { + return sourceAgency; + } + + public String getInRangeMethod() { + return inRangeMethod; + } + + public String getOutRangeLowMethod() { + return outRangeLowMethod; + } + + public String getOutRangeHighMethod() { + return outRangeHighMethod; + } + + public String getActive() { + return active; + } + + public String getAutoUpdate() { + return autoUpdate; + } + + public String getAutoActivate() { + return autoActivate; + } + + public String getAutoMigrateExtension() { + return autoMigrateExtension; + } + + public IndRoundingSpecsElement getIndRoundingSpecs() { + return indRoundingSpecs; + } + + public String getDepRoundingSpec() { + return depRoundingSpec; + } + + public String getDescription() { + return description; + } + + @JsonPOJOBuilder + public static final class Builder { + private String officeId; + private String ratingSpecId; + private String templateId; + private String locationId; + private String version; + private String sourceAgency; + private String inRangeMethod; + private String outRangeLowMethod; + private String outRangeHighMethod; + private String active; + private String autoUpdate; + private String autoActivate; + private String autoMigrateExtension; + private IndRoundingSpecsElement indRoundingSpecs; + private String depRoundingSpec; + private String description; + + @JsonProperty("office-id") + public Builder withOfficeId(String officeId) { + this.officeId = officeId; + return this; + } + + @JsonProperty("rating-spec-id") + public Builder withRatingSpecId(String ratingSpecId) { + this.ratingSpecId = ratingSpecId; + return this; + } + + @JsonProperty("template-id") + public Builder withTemplateId(String templateId) { + this.templateId = templateId; + return this; + } + + @JsonProperty("location-id") + public Builder withLocationId(String locationId) { + this.locationId = locationId; + return this; + } + + @JsonProperty("version") + public Builder withVersion(String version) { + this.version = version; + return this; + } + + @JsonProperty("source-agency") + public Builder withSourceAgency(String sourceAgency) { + this.sourceAgency = sourceAgency; + return this; + } + + @JsonProperty("in-range-method") + public Builder withInRangeMethod(String inRangeMethod) { + this.inRangeMethod = inRangeMethod; + return this; + } + + @JsonProperty("out-range-low-method") + public Builder withOutRangeLowMethod(String outRangeLowMethod) { + this.outRangeLowMethod = outRangeLowMethod; + return this; + } + + @JsonProperty("out-range-high-method") + public Builder withOutRangeHighMethod(String outRangeHighMethod) { + this.outRangeHighMethod = outRangeHighMethod; + return this; + } + + @JsonProperty("active") + public Builder withActive(String active) { + this.active = active; + return this; + } + + @JsonIgnore + public Builder withActive(boolean active) { + return withActive(String.valueOf(active)); + } + + @JsonProperty("auto-update") + public Builder withAutoUpdate(String autoUpdate) { + this.autoUpdate = autoUpdate; + return this; + } + + @JsonIgnore + public Builder withAutoUpdate(boolean autoUpdate) { + return withAutoUpdate(String.valueOf(autoUpdate)); + } + + @JsonProperty("auto-activate") + public Builder withAutoActivate(String autoActivate) { + this.autoActivate = autoActivate; + return this; + } + + @JsonIgnore + public Builder withAutoActivate(boolean autoActivate) { + return withAutoActivate(String.valueOf(autoActivate)); + } + + @JsonProperty("auto-migrate-extension") + public Builder withAutoMigrateExtension(String autoMigrateExtension) { + this.autoMigrateExtension = autoMigrateExtension; + return this; + } + + @JsonIgnore + public Builder withAutoMigrateExtension(boolean autoMigrateExtension) { + return withAutoMigrateExtension(String.valueOf(autoMigrateExtension)); + } + + @JsonProperty("ind-rounding-specs") + public Builder withIndRoundingSpecs(IndRoundingSpecsElement indRoundingSpecs) { + this.indRoundingSpecs = indRoundingSpecs; + return this; + } + + @JsonProperty("dep-rounding-spec") + public Builder withDepRoundingSpec(String depRoundingSpec) { + this.depRoundingSpec = depRoundingSpec; + return this; + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + public RatingSpecElement build() { + return new RatingSpecElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingTemplateElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingTemplateElement.java new file mode 100644 index 0000000000..06e6506ddc --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingTemplateElement.java @@ -0,0 +1,145 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = RatingTemplateElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office-id", "parameters-id", "version", "ind-parameter-specs", "dep-parameter", + "description"}) +public final class RatingTemplateElement { + + @JsonProperty("office-id") + @JacksonXmlProperty(isAttribute = true, localName = "office-id") + private final String officeId; + + @JsonProperty("parameters-id") + private final String parametersId; + + @JsonProperty("version") + private final String version; + + @JsonProperty("ind-parameter-specs") + private final IndParameterSpecsElement indParameterSpecs; + + @JsonProperty("dep-parameter") + private final String depParameter; + + @JsonProperty("description") + private final String description; + + private RatingTemplateElement(Builder builder) { + this.officeId = builder.officeId; + this.parametersId = builder.parametersId; + this.version = builder.version; + this.indParameterSpecs = builder.indParameterSpecs; + this.depParameter = builder.depParameter; + this.description = builder.description; + } + + public String getOfficeId() { + return officeId; + } + + public String getParametersId() { + return parametersId; + } + + public String getVersion() { + return version; + } + + public IndParameterSpecsElement getIndParameterSpecs() { + return indParameterSpecs; + } + + public String getDepParameter() { + return depParameter; + } + + public String getDescription() { + return description; + } + + @JsonPOJOBuilder + public static final class Builder { + private String officeId; + private String parametersId; + private String version; + private IndParameterSpecsElement indParameterSpecs; + private String depParameter; + private String description; + + @JsonProperty("office-id") + public Builder withOfficeId(String officeId) { + this.officeId = officeId; + return this; + } + + @JsonProperty("parameters-id") + public Builder withParametersId(String parametersId) { + this.parametersId = parametersId; + return this; + } + + @JsonProperty("version") + public Builder withVersion(String version) { + this.version = version; + return this; + } + + @JsonProperty("ind-parameter-specs") + public Builder withIndParameterSpecs(IndParameterSpecsElement indParameterSpecs) { + this.indParameterSpecs = indParameterSpecs; + return this; + } + + @JsonProperty("dep-parameter") + public Builder withDepParameter(String depParameter) { + this.depParameter = depParameter; + return this; + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + public RatingTemplateElement build() { + return new RatingTemplateElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingsDocument.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingsDocument.java new file mode 100644 index 0000000000..6cc17d85aa --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/RatingsDocument.java @@ -0,0 +1,231 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import java.util.List; + +@JacksonXmlRootElement(localName = RatingsDocument.ROOT_ELEMENT) +@JsonDeserialize(builder = RatingsDocument.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({RatingsDocument.SCHEMA_LOCATION, "rating-template", "rating-spec", "simple-rating", + "usgs-stream-rating", "virtual-rating", "transitional-rating"}) +public final class RatingsDocument { + + public static final String ROOT_ELEMENT = "ratings"; + static final String SCHEMA_LOCATION = "noNamespaceSchemaLocation"; + + @JsonProperty(SCHEMA_LOCATION) + @JacksonXmlProperty(isAttribute = true, localName = SCHEMA_LOCATION) + private final String noNamespaceSchemaLocation; + + @JsonProperty("rating-template") + @JacksonXmlElementWrapper(useWrapping = false) + private final List ratingTemplates; + + @JsonProperty("rating-spec") + @JacksonXmlElementWrapper(useWrapping = false) + private final List ratingSpecs; + + @JsonProperty("simple-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private final List simpleRatings; + + @JsonProperty("usgs-stream-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private final List usgsStreamRatings; + + @JsonProperty("virtual-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private final List virtualRatings; + + @JsonProperty("transitional-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private final List transitionalRatings; + + private RatingsDocument(Builder builder) { + this.noNamespaceSchemaLocation = builder.noNamespaceSchemaLocation; + this.ratingTemplates = ListUtil.copyOf(builder.ratingTemplates); + this.ratingSpecs = ListUtil.copyOf(builder.ratingSpecs); + this.simpleRatings = ListUtil.copyOf(builder.simpleRatings); + this.usgsStreamRatings = ListUtil.copyOf(builder.usgsStreamRatings); + this.virtualRatings = ListUtil.copyOf(builder.virtualRatings); + this.transitionalRatings = ListUtil.copyOf(builder.transitionalRatings); + } + + public String getNoNamespaceSchemaLocation() { + return noNamespaceSchemaLocation; + } + + public List getRatingTemplates() { + return ratingTemplates; + } + + public List getRatingSpecs() { + return ratingSpecs; + } + + public List getSimpleRatings() { + return simpleRatings; + } + + public List getUsgsStreamRatings() { + return usgsStreamRatings; + } + + public List getVirtualRatings() { + return virtualRatings; + } + + public List getTransitionalRatings() { + return transitionalRatings; + } + + // Only the explicitly annotated add* methods are used by jackson. + @JsonPOJOBuilder + @JsonAutoDetect(setterVisibility = JsonAutoDetect.Visibility.NONE) + @JsonIgnoreProperties({"schemaLocation"}) + public static final class Builder { + private String noNamespaceSchemaLocation; + private List ratingTemplates; + private List ratingSpecs; + private List simpleRatings; + private List usgsStreamRatings; + private List virtualRatings; + private List transitionalRatings; + + public Builder() { + } + + public Builder(RatingsDocument ratings) { + this.noNamespaceSchemaLocation = ratings.noNamespaceSchemaLocation; + this.ratingTemplates = ratings.ratingTemplates; + this.ratingSpecs = ratings.ratingSpecs; + this.simpleRatings = ratings.simpleRatings; + this.usgsStreamRatings = ratings.usgsStreamRatings; + this.virtualRatings = ratings.virtualRatings; + this.transitionalRatings = ratings.transitionalRatings; + } + + @JsonProperty(SCHEMA_LOCATION) + public Builder withNoNamespaceSchemaLocation(String noNamespaceSchemaLocation) { + this.noNamespaceSchemaLocation = noNamespaceSchemaLocation; + return this; + } + + public Builder withRatingTemplates(List ratingTemplates) { + this.ratingTemplates = ratingTemplates; + return this; + } + + public Builder withRatingSpecs(List ratingSpecs) { + this.ratingSpecs = ratingSpecs; + return this; + } + + public Builder withSimpleRatings(List simpleRatings) { + this.simpleRatings = simpleRatings; + return this; + } + + public Builder withUsgsStreamRatings(List usgsStreamRatings) { + this.usgsStreamRatings = usgsStreamRatings; + return this; + } + + public Builder withVirtualRatings(List virtualRatings) { + this.virtualRatings = virtualRatings; + return this; + } + + public Builder withTransitionalRatings(List transitionalRatings) { + this.transitionalRatings = transitionalRatings; + return this; + } + + // Jackson calls these once per contiguous run of an element, so they append rather than + // replace to support interleaved rating types. + + @JsonProperty("rating-template") + @JacksonXmlElementWrapper(useWrapping = false) + private Builder addRatingTemplates(List values) { + this.ratingTemplates = ListUtil.append(this.ratingTemplates, values); + return this; + } + + @JsonProperty("rating-spec") + @JacksonXmlElementWrapper(useWrapping = false) + private Builder addRatingSpecs(List values) { + this.ratingSpecs = ListUtil.append(this.ratingSpecs, values); + return this; + } + + @JsonProperty("simple-rating") + @JsonAlias("rating") + @JacksonXmlElementWrapper(useWrapping = false) + private Builder addSimpleRatings(List values) { + this.simpleRatings = ListUtil.append(this.simpleRatings, values); + return this; + } + + @JsonProperty("usgs-stream-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private Builder addUsgsStreamRatings(List values) { + this.usgsStreamRatings = ListUtil.append(this.usgsStreamRatings, values); + return this; + } + + @JsonProperty("virtual-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private Builder addVirtualRatings(List values) { + this.virtualRatings = ListUtil.append(this.virtualRatings, values); + return this; + } + + @JsonProperty("transitional-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private Builder addTransitionalRatings(List values) { + this.transitionalRatings = ListUtil.append(this.transitionalRatings, values); + return this; + } + + public RatingsDocument build() { + return new RatingsDocument(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SelectElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SelectElement.java new file mode 100644 index 0000000000..ec30fe75ca --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SelectElement.java @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = SelectElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"case", "default"}) +public final class SelectElement { + + @JsonProperty("case") + @JacksonXmlElementWrapper(useWrapping = false) + private final List cases; + + @JsonProperty("default") + private final String defaultValue; + + private SelectElement(Builder builder) { + this.cases = ListUtil.copyOf(builder.cases); + this.defaultValue = builder.defaultValue; + } + + public List getCases() { + return cases; + } + + public String getDefault() { + return defaultValue; + } + + @JsonPOJOBuilder + public static final class Builder { + private List cases; + private String defaultValue; + + @JsonProperty("case") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withCases(List cases) { + this.cases = cases; + return this; + } + + @JsonProperty("default") + public Builder withDefault(String defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public SelectElement build() { + return new SelectElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SimpleRatingElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SimpleRatingElement.java new file mode 100644 index 0000000000..6469899d66 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SimpleRatingElement.java @@ -0,0 +1,248 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import java.util.List; + +@JsonDeserialize(builder = SimpleRatingElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office-id", "rating-spec-id", "vertical-datum-info", "units-id", + "effective-date", "transition-start-date", "create-date", "active", "description", "formula", + "rating-points", "extension-points"}) +public final class SimpleRatingElement { + + @JsonProperty("office-id") + @JacksonXmlProperty(isAttribute = true, localName = "office-id") + private final String officeId; + + @JsonProperty("rating-spec-id") + private final String ratingSpecId; + + @JsonProperty("vertical-datum-info") + private final VerticalDatumInfoElement verticalDatumInfo; + + @JsonProperty("units-id") + private final String unitsId; + + @JsonProperty("effective-date") + private final String effectiveDate; + + @JsonProperty("transition-start-date") + private final String transitionStartDate; + + @JsonProperty("create-date") + private final String createDate; + + @JsonProperty("active") + private final String active; + + @JsonProperty("description") + private final String description; + + @JsonProperty("formula") + private final String formula; + + @JsonProperty("rating-points") + @JacksonXmlElementWrapper(useWrapping = false) + private final List ratingPoints; + + @JsonProperty("extension-points") + @JacksonXmlElementWrapper(useWrapping = false) + private final List extensionPoints; + + private SimpleRatingElement(Builder builder) { + this.officeId = builder.officeId; + this.ratingSpecId = builder.ratingSpecId; + this.verticalDatumInfo = builder.verticalDatumInfo; + this.unitsId = builder.unitsId; + this.effectiveDate = builder.effectiveDate; + this.transitionStartDate = builder.transitionStartDate; + this.createDate = builder.createDate; + this.active = builder.active; + this.description = builder.description; + this.formula = builder.formula; + this.ratingPoints = ListUtil.copyOf(builder.ratingPoints); + this.extensionPoints = ListUtil.copyOf(builder.extensionPoints); + } + + public String getOfficeId() { + return officeId; + } + + public String getRatingSpecId() { + return ratingSpecId; + } + + public VerticalDatumInfoElement getVerticalDatumInfo() { + return verticalDatumInfo; + } + + public String getUnitsId() { + return unitsId; + } + + public String getEffectiveDate() { + return effectiveDate; + } + + public String getTransitionStartDate() { + return transitionStartDate; + } + + public String getCreateDate() { + return createDate; + } + + public String getActive() { + return active; + } + + public String getDescription() { + return description; + } + + public String getFormula() { + return formula; + } + + public List getRatingPoints() { + return ratingPoints; + } + + public List getExtensionPoints() { + return extensionPoints; + } + + @JsonPOJOBuilder + public static final class Builder { + private String officeId; + private String ratingSpecId; + private VerticalDatumInfoElement verticalDatumInfo; + private String unitsId; + private String effectiveDate; + private String transitionStartDate; + private String createDate; + private String active; + private String description; + private String formula; + private List ratingPoints; + private List extensionPoints; + + @JsonProperty("office-id") + public Builder withOfficeId(String officeId) { + this.officeId = officeId; + return this; + } + + @JsonProperty("rating-spec-id") + public Builder withRatingSpecId(String ratingSpecId) { + this.ratingSpecId = ratingSpecId; + return this; + } + + @JsonProperty("vertical-datum-info") + public Builder withVerticalDatumInfo(VerticalDatumInfoElement verticalDatumInfo) { + this.verticalDatumInfo = verticalDatumInfo; + return this; + } + + @JsonProperty("units-id") + public Builder withUnitsId(String unitsId) { + this.unitsId = unitsId; + return this; + } + + @JsonProperty("effective-date") + public Builder withEffectiveDate(String effectiveDate) { + this.effectiveDate = effectiveDate; + return this; + } + + @JsonProperty("transition-start-date") + public Builder withTransitionStartDate(String transitionStartDate) { + this.transitionStartDate = transitionStartDate; + return this; + } + + @JsonProperty("create-date") + public Builder withCreateDate(String createDate) { + this.createDate = createDate; + return this; + } + + @JsonProperty("active") + public Builder withActive(String active) { + this.active = active; + return this; + } + + @JsonIgnore + public Builder withActive(boolean active) { + return withActive(String.valueOf(active)); + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + @JsonProperty("formula") + public Builder withFormula(String formula) { + this.formula = formula; + return this; + } + + @JsonProperty("rating-points") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withRatingPoints(List ratingPoints) { + this.ratingPoints = ratingPoints; + return this; + } + + @JsonProperty("extension-points") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withExtensionPoints(List extensionPoints) { + this.extensionPoints = extensionPoints; + return this; + } + + public SimpleRatingElement build() { + return new SimpleRatingElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SourceRatingElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SourceRatingElement.java new file mode 100644 index 0000000000..c8e224563e --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/SourceRatingElement.java @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = SourceRatingElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"position", "rating-spec-id", "rating-expression"}) +public final class SourceRatingElement { + + @JsonProperty("position") + @JacksonXmlProperty(isAttribute = true, localName = "position") + private final String position; + + @JsonProperty("rating-spec-id") + private final String ratingSpecId; + + @JsonProperty("rating-expression") + private final String ratingExpression; + + private SourceRatingElement(Builder builder) { + this.position = builder.position; + this.ratingSpecId = builder.ratingSpecId; + this.ratingExpression = builder.ratingExpression; + } + + public String getPosition() { + return position; + } + + public String getRatingSpecId() { + return ratingSpecId; + } + + public String getRatingExpression() { + return ratingExpression; + } + + @JsonPOJOBuilder + public static final class Builder { + private String position; + private String ratingSpecId; + private String ratingExpression; + + @JsonProperty("position") + public Builder withPosition(String position) { + this.position = position; + return this; + } + + @JsonIgnore + public Builder withPosition(int position) { + return withPosition(String.valueOf(position)); + } + + @JsonProperty("rating-spec-id") + public Builder withRatingSpecId(String ratingSpecId) { + this.ratingSpecId = ratingSpecId; + return this; + } + + @JsonProperty("rating-expression") + public Builder withRatingExpression(String ratingExpression) { + this.ratingExpression = ratingExpression; + return this; + } + + public SourceRatingElement build() { + return new SourceRatingElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalRatingElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalRatingElement.java new file mode 100644 index 0000000000..ad5f1f0f93 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalRatingElement.java @@ -0,0 +1,211 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = TransitionalRatingElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office-id", "rating-spec-id", "units-id", "effective-date", + "transition-start-date", "create-date", "active", "description", "select", "source-ratings"}) +public final class TransitionalRatingElement { + + @JsonProperty("office-id") + @JacksonXmlProperty(isAttribute = true, localName = "office-id") + private final String officeId; + + @JsonProperty("rating-spec-id") + private final String ratingSpecId; + + @JsonProperty("units-id") + private final String unitsId; + + @JsonProperty("effective-date") + private final String effectiveDate; + + @JsonProperty("transition-start-date") + private final String transitionStartDate; + + @JsonProperty("create-date") + private final String createDate; + + @JsonProperty("active") + private final String active; + + @JsonProperty("description") + private final String description; + + @JsonProperty("select") + private final SelectElement select; + + @JsonProperty("source-ratings") + private final TransitionalSourceRatingsElement sourceRatings; + + private TransitionalRatingElement(Builder builder) { + this.officeId = builder.officeId; + this.ratingSpecId = builder.ratingSpecId; + this.unitsId = builder.unitsId; + this.effectiveDate = builder.effectiveDate; + this.transitionStartDate = builder.transitionStartDate; + this.createDate = builder.createDate; + this.active = builder.active; + this.description = builder.description; + this.select = builder.select; + this.sourceRatings = builder.sourceRatings; + } + + public String getOfficeId() { + return officeId; + } + + public String getRatingSpecId() { + return ratingSpecId; + } + + public String getUnitsId() { + return unitsId; + } + + public String getEffectiveDate() { + return effectiveDate; + } + + public String getTransitionStartDate() { + return transitionStartDate; + } + + public String getCreateDate() { + return createDate; + } + + public String getActive() { + return active; + } + + public String getDescription() { + return description; + } + + public SelectElement getSelect() { + return select; + } + + public TransitionalSourceRatingsElement getSourceRatings() { + return sourceRatings; + } + + @JsonPOJOBuilder + public static final class Builder { + private String officeId; + private String ratingSpecId; + private String unitsId; + private String effectiveDate; + private String transitionStartDate; + private String createDate; + private String active; + private String description; + private SelectElement select; + private TransitionalSourceRatingsElement sourceRatings; + + @JsonProperty("office-id") + public Builder withOfficeId(String officeId) { + this.officeId = officeId; + return this; + } + + @JsonProperty("rating-spec-id") + public Builder withRatingSpecId(String ratingSpecId) { + this.ratingSpecId = ratingSpecId; + return this; + } + + @JsonProperty("units-id") + public Builder withUnitsId(String unitsId) { + this.unitsId = unitsId; + return this; + } + + @JsonProperty("effective-date") + public Builder withEffectiveDate(String effectiveDate) { + this.effectiveDate = effectiveDate; + return this; + } + + @JsonProperty("transition-start-date") + public Builder withTransitionStartDate(String transitionStartDate) { + this.transitionStartDate = transitionStartDate; + return this; + } + + @JsonProperty("create-date") + public Builder withCreateDate(String createDate) { + this.createDate = createDate; + return this; + } + + @JsonProperty("active") + public Builder withActive(String active) { + this.active = active; + return this; + } + + @JsonIgnore + public Builder withActive(boolean active) { + return withActive(String.valueOf(active)); + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + @JsonProperty("select") + public Builder withSelect(SelectElement select) { + this.select = select; + return this; + } + + @JsonProperty("source-ratings") + public Builder withSourceRatings(TransitionalSourceRatingsElement sourceRatings) { + this.sourceRatings = sourceRatings; + return this; + } + + public TransitionalRatingElement build() { + return new TransitionalRatingElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalSourceRatingsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalSourceRatingsElement.java new file mode 100644 index 0000000000..7332fc6d0e --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/TransitionalSourceRatingsElement.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = TransitionalSourceRatingsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class TransitionalSourceRatingsElement { + + @JsonProperty("rating-spec-id") + @JacksonXmlElementWrapper(useWrapping = false) + private final List ratingSpecIds; + + private TransitionalSourceRatingsElement(Builder builder) { + this.ratingSpecIds = ListUtil.copyOf(builder.ratingSpecIds); + } + + public List getRatingSpecIds() { + return ratingSpecIds; + } + + @JsonPOJOBuilder + public static final class Builder { + private List ratingSpecIds; + + @JsonProperty("rating-spec-id") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withRatingSpecIds(List ratingSpecIds) { + this.ratingSpecIds = ratingSpecIds; + return this; + } + + public TransitionalSourceRatingsElement build() { + return new TransitionalSourceRatingsElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/UsgsStreamRatingElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/UsgsStreamRatingElement.java new file mode 100644 index 0000000000..dcb4eef528 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/UsgsStreamRatingElement.java @@ -0,0 +1,265 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import java.util.List; + +@JsonDeserialize(builder = UsgsStreamRatingElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office-id", "rating-spec-id", "vertical-datum-info", "units-id", + "effective-date", "transition-start-date", "create-date", "active", "description", + "height-shifts", "height-offsets", "rating-points", "extension-points"}) +public final class UsgsStreamRatingElement { + + @JsonProperty("office-id") + @JacksonXmlProperty(isAttribute = true, localName = "office-id") + private final String officeId; + + @JsonProperty("rating-spec-id") + private final String ratingSpecId; + + @JsonProperty("vertical-datum-info") + private final VerticalDatumInfoElement verticalDatumInfo; + + @JsonProperty("units-id") + private final String unitsId; + + @JsonProperty("effective-date") + private final String effectiveDate; + + @JsonProperty("transition-start-date") + private final String transitionStartDate; + + @JsonProperty("create-date") + private final String createDate; + + @JsonProperty("active") + private final String active; + + @JsonProperty("description") + private final String description; + + @JsonProperty("height-shifts") + @JacksonXmlElementWrapper(useWrapping = false) + private final List heightShifts; + + @JsonProperty("height-offsets") + private final HeightOffsetsElement heightOffsets; + + @JsonProperty("rating-points") + @JacksonXmlElementWrapper(useWrapping = false) + private final List ratingPoints; + + @JsonProperty("extension-points") + @JacksonXmlElementWrapper(useWrapping = false) + private final List extensionPoints; + + private UsgsStreamRatingElement(Builder builder) { + this.officeId = builder.officeId; + this.ratingSpecId = builder.ratingSpecId; + this.verticalDatumInfo = builder.verticalDatumInfo; + this.unitsId = builder.unitsId; + this.effectiveDate = builder.effectiveDate; + this.transitionStartDate = builder.transitionStartDate; + this.createDate = builder.createDate; + this.active = builder.active; + this.description = builder.description; + this.heightShifts = ListUtil.copyOf(builder.heightShifts); + this.heightOffsets = builder.heightOffsets; + this.ratingPoints = ListUtil.copyOf(builder.ratingPoints); + this.extensionPoints = ListUtil.copyOf(builder.extensionPoints); + } + + public String getOfficeId() { + return officeId; + } + + public String getRatingSpecId() { + return ratingSpecId; + } + + public VerticalDatumInfoElement getVerticalDatumInfo() { + return verticalDatumInfo; + } + + public String getUnitsId() { + return unitsId; + } + + public String getEffectiveDate() { + return effectiveDate; + } + + public String getTransitionStartDate() { + return transitionStartDate; + } + + public String getCreateDate() { + return createDate; + } + + public String getActive() { + return active; + } + + public String getDescription() { + return description; + } + + public List getHeightShifts() { + return heightShifts; + } + + public HeightOffsetsElement getHeightOffsets() { + return heightOffsets; + } + + public List getRatingPoints() { + return ratingPoints; + } + + public List getExtensionPoints() { + return extensionPoints; + } + + @JsonPOJOBuilder + public static final class Builder { + private String officeId; + private String ratingSpecId; + private VerticalDatumInfoElement verticalDatumInfo; + private String unitsId; + private String effectiveDate; + private String transitionStartDate; + private String createDate; + private String active; + private String description; + private List heightShifts; + private HeightOffsetsElement heightOffsets; + private List ratingPoints; + private List extensionPoints; + + @JsonProperty("office-id") + public Builder withOfficeId(String officeId) { + this.officeId = officeId; + return this; + } + + @JsonProperty("rating-spec-id") + public Builder withRatingSpecId(String ratingSpecId) { + this.ratingSpecId = ratingSpecId; + return this; + } + + @JsonProperty("vertical-datum-info") + public Builder withVerticalDatumInfo(VerticalDatumInfoElement verticalDatumInfo) { + this.verticalDatumInfo = verticalDatumInfo; + return this; + } + + @JsonProperty("units-id") + public Builder withUnitsId(String unitsId) { + this.unitsId = unitsId; + return this; + } + + @JsonProperty("effective-date") + public Builder withEffectiveDate(String effectiveDate) { + this.effectiveDate = effectiveDate; + return this; + } + + @JsonProperty("transition-start-date") + public Builder withTransitionStartDate(String transitionStartDate) { + this.transitionStartDate = transitionStartDate; + return this; + } + + @JsonProperty("create-date") + public Builder withCreateDate(String createDate) { + this.createDate = createDate; + return this; + } + + @JsonProperty("active") + public Builder withActive(String active) { + this.active = active; + return this; + } + + @JsonIgnore + public Builder withActive(boolean active) { + return withActive(String.valueOf(active)); + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + @JsonProperty("height-shifts") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withHeightShifts(List heightShifts) { + this.heightShifts = heightShifts; + return this; + } + + @JsonProperty("height-offsets") + public Builder withHeightOffsets(HeightOffsetsElement heightOffsets) { + this.heightOffsets = heightOffsets; + return this; + } + + @JsonProperty("rating-points") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withRatingPoints(List ratingPoints) { + this.ratingPoints = ratingPoints; + return this; + } + + @JsonProperty("extension-points") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withExtensionPoints(List extensionPoints) { + this.extensionPoints = extensionPoints; + return this; + } + + public UsgsStreamRatingElement build() { + return new UsgsStreamRatingElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumInfoElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumInfoElement.java new file mode 100644 index 0000000000..00b1498dae --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumInfoElement.java @@ -0,0 +1,165 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import java.util.List; + +@JsonDeserialize(builder = VerticalDatumInfoElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office", "unit", "location", "native-datum", "local-datum-name", "elevation", + "offset"}) +public final class VerticalDatumInfoElement { + + @JsonProperty("office") + @JacksonXmlProperty(isAttribute = true, localName = "office") + private final String office; + + @JsonProperty("unit") + @JacksonXmlProperty(isAttribute = true, localName = "unit") + private final String unit; + + @JsonProperty("location") + private final String location; + + @JsonProperty("native-datum") + private final String nativeDatum; + + @JsonProperty("local-datum-name") + private final String localDatumName; + + @JsonProperty("elevation") + private final String elevation; + + @JsonProperty("offset") + @JacksonXmlElementWrapper(useWrapping = false) + private final List offsets; + + private VerticalDatumInfoElement(Builder builder) { + this.office = builder.office; + this.unit = builder.unit; + this.location = builder.location; + this.nativeDatum = builder.nativeDatum; + this.localDatumName = builder.localDatumName; + this.elevation = builder.elevation; + this.offsets = ListUtil.copyOf(builder.offsets); + } + + public String getOffice() { + return office; + } + + public String getUnit() { + return unit; + } + + public String getLocation() { + return location; + } + + public String getNativeDatum() { + return nativeDatum; + } + + public String getLocalDatumName() { + return localDatumName; + } + + public String getElevation() { + return elevation; + } + + public List getOffsets() { + return offsets; + } + + @JsonPOJOBuilder + public static final class Builder { + private String office; + private String unit; + private String location; + private String nativeDatum; + private String localDatumName; + private String elevation; + private List offsets; + + @JsonProperty("office") + public Builder withOffice(String office) { + this.office = office; + return this; + } + + @JsonProperty("unit") + public Builder withUnit(String unit) { + this.unit = unit; + return this; + } + + @JsonProperty("location") + public Builder withLocation(String location) { + this.location = location; + return this; + } + + @JsonProperty("native-datum") + public Builder withNativeDatum(String nativeDatum) { + this.nativeDatum = nativeDatum; + return this; + } + + @JsonProperty("local-datum-name") + public Builder withLocalDatumName(String localDatumName) { + this.localDatumName = localDatumName; + return this; + } + + @JsonProperty("elevation") + public Builder withElevation(String elevation) { + this.elevation = elevation; + return this; + } + + @JsonProperty("offset") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withOffsets(List offsets) { + this.offsets = offsets; + return this; + } + + public VerticalDatumInfoElement build() { + return new VerticalDatumInfoElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumOffsetElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumOffsetElement.java new file mode 100644 index 0000000000..7686b450c9 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VerticalDatumOffsetElement.java @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = VerticalDatumOffsetElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"estimate", "to-datum", "value"}) +public final class VerticalDatumOffsetElement { + + @JsonProperty("estimate") + @JacksonXmlProperty(isAttribute = true, localName = "estimate") + private final String estimate; + + @JsonProperty("to-datum") + private final String toDatum; + + @JsonProperty("value") + private final String value; + + private VerticalDatumOffsetElement(Builder builder) { + this.estimate = builder.estimate; + this.toDatum = builder.toDatum; + this.value = builder.value; + } + + public String getEstimate() { + return estimate; + } + + public String getToDatum() { + return toDatum; + } + + public String getValue() { + return value; + } + + @JsonPOJOBuilder + public static final class Builder { + private String estimate; + private String toDatum; + private String value; + + @JsonProperty("estimate") + public Builder withEstimate(String estimate) { + this.estimate = estimate; + return this; + } + + @JsonIgnore + public Builder withEstimate(boolean estimate) { + return withEstimate(String.valueOf(estimate)); + } + + @JsonProperty("to-datum") + public Builder withToDatum(String toDatum) { + this.toDatum = toDatum; + return this; + } + + @JsonProperty("value") + public Builder withValue(String value) { + this.value = value; + return this; + } + + public VerticalDatumOffsetElement build() { + return new VerticalDatumOffsetElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualRatingElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualRatingElement.java new file mode 100644 index 0000000000..f5647da9f8 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualRatingElement.java @@ -0,0 +1,196 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonDeserialize(builder = VirtualRatingElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({"office-id", "rating-spec-id", "effective-date", "transition-start-date", + "create-date", "active", "description", "connections", "source-ratings"}) +public final class VirtualRatingElement { + + @JsonProperty("office-id") + @JacksonXmlProperty(isAttribute = true, localName = "office-id") + private final String officeId; + + @JsonProperty("rating-spec-id") + private final String ratingSpecId; + + @JsonProperty("effective-date") + private final String effectiveDate; + + @JsonProperty("transition-start-date") + private final String transitionStartDate; + + @JsonProperty("create-date") + private final String createDate; + + @JsonProperty("active") + private final String active; + + @JsonProperty("description") + private final String description; + + @JsonProperty("connections") + private final String connections; + + @JsonProperty("source-ratings") + private final VirtualSourceRatingsElement sourceRatings; + + private VirtualRatingElement(Builder builder) { + this.officeId = builder.officeId; + this.ratingSpecId = builder.ratingSpecId; + this.effectiveDate = builder.effectiveDate; + this.transitionStartDate = builder.transitionStartDate; + this.createDate = builder.createDate; + this.active = builder.active; + this.description = builder.description; + this.connections = builder.connections; + this.sourceRatings = builder.sourceRatings; + } + + public String getOfficeId() { + return officeId; + } + + public String getRatingSpecId() { + return ratingSpecId; + } + + public String getEffectiveDate() { + return effectiveDate; + } + + public String getTransitionStartDate() { + return transitionStartDate; + } + + public String getCreateDate() { + return createDate; + } + + public String getActive() { + return active; + } + + public String getDescription() { + return description; + } + + public String getConnections() { + return connections; + } + + public VirtualSourceRatingsElement getSourceRatings() { + return sourceRatings; + } + + @JsonPOJOBuilder + public static final class Builder { + private String officeId; + private String ratingSpecId; + private String effectiveDate; + private String transitionStartDate; + private String createDate; + private String active; + private String description; + private String connections; + private VirtualSourceRatingsElement sourceRatings; + + @JsonProperty("office-id") + public Builder withOfficeId(String officeId) { + this.officeId = officeId; + return this; + } + + @JsonProperty("rating-spec-id") + public Builder withRatingSpecId(String ratingSpecId) { + this.ratingSpecId = ratingSpecId; + return this; + } + + @JsonProperty("effective-date") + public Builder withEffectiveDate(String effectiveDate) { + this.effectiveDate = effectiveDate; + return this; + } + + @JsonProperty("transition-start-date") + public Builder withTransitionStartDate(String transitionStartDate) { + this.transitionStartDate = transitionStartDate; + return this; + } + + @JsonProperty("create-date") + public Builder withCreateDate(String createDate) { + this.createDate = createDate; + return this; + } + + @JsonProperty("active") + public Builder withActive(String active) { + this.active = active; + return this; + } + + @JsonIgnore + public Builder withActive(boolean active) { + return withActive(String.valueOf(active)); + } + + @JsonProperty("description") + public Builder withDescription(String description) { + this.description = description; + return this; + } + + @JsonProperty("connections") + public Builder withConnections(String connections) { + this.connections = connections; + return this; + } + + @JsonProperty("source-ratings") + public Builder withSourceRatings(VirtualSourceRatingsElement sourceRatings) { + this.sourceRatings = sourceRatings; + return this; + } + + public VirtualRatingElement build() { + return new VirtualRatingElement(this); + } + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualSourceRatingsElement.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualSourceRatingsElement.java new file mode 100644 index 0000000000..d9d5bc4fe9 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/rating/document/VirtualSourceRatingsElement.java @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2026 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dto.rating.document; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import java.util.List; + +@JsonDeserialize(builder = VirtualSourceRatingsElement.Builder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonInclude(JsonInclude.Include.NON_NULL) +public final class VirtualSourceRatingsElement { + + @JsonProperty("source-rating") + @JacksonXmlElementWrapper(useWrapping = false) + private final List sourceRatings; + + private VirtualSourceRatingsElement(Builder builder) { + this.sourceRatings = ListUtil.copyOf(builder.sourceRatings); + } + + public List getSourceRatings() { + return sourceRatings; + } + + @JsonPOJOBuilder + public static final class Builder { + private List sourceRatings; + + @JsonProperty("source-rating") + @JacksonXmlElementWrapper(useWrapping = false) + public Builder withSourceRatings(List sourceRatings) { + this.sourceRatings = sourceRatings; + return this; + } + + public VirtualSourceRatingsElement build() { + return new VirtualSourceRatingsElement(this); + } + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dao/JsonRatingUtilsTest.java b/cwms-data-api/src/test/java/cwms/cda/data/dao/JsonRatingUtilsTest.java index 702835120c..ef04331e18 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dao/JsonRatingUtilsTest.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dao/JsonRatingUtilsTest.java @@ -1,8 +1,17 @@ package cwms.cda.data.dao; -import static cwms.cda.data.dao.JsonRatingUtils.buildSourceFromResource; import static org.junit.jupiter.api.Assertions.*; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import cwms.cda.data.dto.rating.document.PositionalValueElement; +import cwms.cda.data.dto.rating.document.RatingPointsElement; +import cwms.cda.data.dto.rating.document.RatingSpecElement; +import cwms.cda.data.dto.rating.document.RatingsDocument; +import cwms.cda.data.dto.rating.document.SimpleRatingElement; +import cwms.cda.data.dto.rating.document.TransitionalRatingElement; +import cwms.cda.data.dto.rating.document.VirtualRatingElement; import hec.data.RatingException; import hec.data.cwmsRating.RatingSet; import java.io.ByteArrayOutputStream; @@ -15,9 +24,8 @@ import mil.army.usace.hec.cwms.rating.io.xml.RatingXmlFactory; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; - -import javax.xml.transform.Source; -import javax.xml.transform.TransformerException; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; public class JsonRatingUtilsTest { @@ -153,22 +161,215 @@ void test_just_ind() throws IOException, RatingException { } @Test - void test_json_to_xml_ind() throws IOException, TransformerException { + void test_json_to_xml_ind() throws IOException { String json = loadResourceAsString("cwms/cda/api/spk/ratings_ind.json"); String asXml = JsonRatingUtils.jsonToXml(json); assertTrue(asXml.contains("other-ind position=\"2\"")); + // attributes, not child elements + assertTrue(asXml.contains("")); + assertTrue(asXml.contains("2222233332")); + assertTrue(asXml.contains("")); + assertFalse(asXml.contains("element-value")); + assertFalse(asXml.contains("noNamespaceSchemaLocation")); + } + + @Test + void test_read_json_dto() throws IOException { + String json = loadResourceAsString("cwms/cda/api/spk/ratings_ind.json"); + RatingsDocument ratings = JsonRatingUtils.readJson(json); + assertDtoMatchesRatingsInd(ratings); + } + + @Test + void test_read_xml_dto() throws IOException { + String xml = loadResourceAsString("cwms/cda/api/spk/ratings_ind.xml"); + RatingsDocument ratings = JsonRatingUtils.readXml(xml); + assertDtoMatchesRatingsInd(ratings); + } + + private static void assertDtoMatchesRatingsInd(RatingsDocument ratings) { + assertEquals(1, ratings.getRatingTemplates().size()); + assertEquals("SPK", ratings.getRatingTemplates().get(0).getOfficeId()); + assertEquals(3, ratings.getRatingTemplates().get(0).getIndParameterSpecs() + .getIndParameterSpecs().size()); + + RatingSpecElement spec = ratings.getRatingSpecs().get(0); + assertEquals("Barren", spec.getLocationId()); + assertEquals("", spec.getSourceAgency()); + PositionalValueElement rounding = spec.getIndRoundingSpecs().getIndRoundingSpecs().get(1); + assertEquals("2", rounding.getPosition()); + assertEquals("2222233332", rounding.getValue()); + + SimpleRatingElement simple = ratings.getSimpleRatings().get(0); + assertEquals("SPK", simple.getOfficeId()); + assertEquals("ft", simple.getVerticalDatumInfo().getUnit()); + assertEquals("NGVD-29", simple.getVerticalDatumInfo().getNativeDatum()); + assertEquals(2, simple.getVerticalDatumInfo().getOffsets().size()); + assertEquals("true", simple.getVerticalDatumInfo().getOffsets().get(1).getEstimate()); + assertEquals("-0.4403", simple.getVerticalDatumInfo().getOffsets().get(1).getValue()); + + RatingPointsElement points = simple.getRatingPoints().get(0); + assertEquals("2", points.getOtherInds().get(1).getPosition()); + assertEquals("0.0", points.getOtherInds().get(1).getValue()); + assertEquals("510.0", points.getPoints().get(0).getInd()); + } + + @ParameterizedTest + @ValueSource(strings = { + "cwms/cda/api/spk/ratings_ind.xml", + "cwms/cda/data/dao/rating.xml.gz", + "cwms/cda/data/dao/ECMT_Stage_Stage_Linear_StepCorrections.xml", + "cwms/cda/data/dao/Black_Rascal_Div_Stage_Flow_USGS-EXSA_Production.xml", + "cwms/cda/data/dao/BEAV.Stage_Flow.BASE.PRODUCTION.xml", + "cwms/cda/data/dao/Farmington_Dam-Gate_1_Opening-Gate_Elev_Flow_Standard_Production.xml", + "cwms/cda/api/STJ-St_Joseph-Missouri_Stage_Flow_USGS-BASE_Production_2.xml", + }) + void test_json_matches_legacy_format(String resource) throws IOException { + String xml = loadResourceAsString(resource); + + String legacyJson = new ObjectMapper().writeValueAsString(new XmlMapper().readTree(xml)) + .replace("\"\":", "\"element-value\":"); + JsonNode expected = new ObjectMapper().readTree(legacyJson); + + JsonNode actual = new ObjectMapper().readTree(JsonRatingUtils.xmlToJson(xml)); + + assertEquals(expected, actual); + } + + @Test + void test_single_element_lists_unwrapped_in_json() throws IOException { + String xml = loadResourceAsString("cwms/cda/data/dao/ECMT_Stage_Stage_Linear_StepCorrections.xml"); + String json = JsonRatingUtils.xmlToJson(xml); + JsonNode node = new ObjectMapper().readTree(json); + + // only one of each in the document so they are objects, not arrays + assertTrue(node.get("rating-template").isObject()); + assertTrue(node.get("simple-rating").isObject()); + assertTrue(node.get("rating-spec").get("ind-rounding-specs").get("ind-rounding-spec").isObject()); + assertTrue(node.get("simple-rating").get("rating-points").get("point").isArray()); + // and they can be read back either as an object or a single element array + RatingsDocument ratings = JsonRatingUtils.readJson(json); + assertEquals(1, ratings.getSimpleRatings().size()); + assertEquals(1, ratings.getRatingSpecs().get(0).getIndRoundingSpecs().getIndRoundingSpecs().size()); } @Test - void test_move_value() throws IOException, TransformerException { - String xml = loadResourceAsString("cwms/cda/api/spk/pre_move_value.xml"); - assertTrue(xml.contains("")); + void test_empty_element_is_empty_string_in_json() throws IOException { + String xml = "" + + "" + + "A.Stage;Flow.Logarithmic.USGS-NWIS" + + "ft;cfs" + + "2013-10-01T04:00:00Z" + + "true" + + "" + + "" + + "" + + ""; - Source xslt = buildSourceFromResource("move_value.xsl"); - String xformed = JsonRatingUtils.applyTransform(xml, xslt); + String json = JsonRatingUtils.xmlToJson(xml); + JsonNode usgs = new ObjectMapper().readTree(json).get("usgs-stream-rating"); + assertTrue(usgs.get("rating-points").isTextual()); + assertEquals("", usgs.get("rating-points").asText()); + assertTrue(usgs.get("height-offsets").isTextual()); + assertEquals("", usgs.get("height-offsets").asText()); - assertTrue(xformed.contains("other-ind position=\"2\"")); + RatingsDocument ratings = JsonRatingUtils.readJson(json); + assertEquals(1, ratings.getUsgsStreamRatings().get(0).getRatingPoints().size()); + assertNotNull(ratings.getUsgsStreamRatings().get(0).getHeightOffsets()); + + RatingsDocument reread = JsonRatingUtils.readXml(JsonRatingUtils.writeXml(ratings)); + assertEquals(1, reread.getUsgsStreamRatings().get(0).getRatingPoints().size()); + assertNotNull(reread.getUsgsStreamRatings().get(0).getHeightOffsets()); + } + + @Test + void test_interleaved_rating_types_are_accumulated() throws IOException { + String xml = "" + + "A.Stage;Flow.Linear.Production" + + "2020-01-01T00:00:00Ztrue" + + "i1 * 2" + + "A.Stage;Flow.Virtual.Production" + + "2021-01-01T00:00:00Ztrue" + + "R2I1=R1D" + + "A.Stage;Stage.Linear.Production {ft;ft}" + + "I1 * 2 {ft;cfs}" + + "" + + "A.Stage;Flow.Linear.Production" + + "2022-01-01T00:00:00Ztrue" + + "i1 * 3" + + ""; + + RatingsDocument ratings = JsonRatingUtils.readXml(xml); + assertEquals(2, ratings.getSimpleRatings().size()); + assertEquals("i1 * 3", ratings.getSimpleRatings().get(1).getFormula()); + + VirtualRatingElement virtual = ratings.getVirtualRatings().get(0); + assertEquals("R2I1=R1D", virtual.getConnections()); + assertEquals(2, virtual.getSourceRatings().getSourceRatings().size()); + assertEquals("I1 * 2 {ft;cfs}", virtual.getSourceRatings().getSourceRatings().get(1).getRatingExpression()); + } + + @Test + void test_transitional_rating_round_trip() throws IOException { + String xml = "" + + "" + + "A.Stage;Flow.Transitional.Production" + + "ft;cfs" + + "1900-01-01T00:00:00-06:00" + + "" + + "true" + + "" + + "" + + "A.Stage;Flow.Linear.Dummy" + + "A.Stage;Flow.Linear.Production" + + "" + + "" + + ""; + + RatingsDocument ratings = JsonRatingUtils.readXml(xml); + TransitionalRatingElement transitional = ratings.getTransitionalRatings().get(0); + assertEquals("", transitional.getTransitionStartDate()); + assertEquals("I1 GT 25", transitional.getSelect().getCases().get(0).getWhen()); + assertEquals("R2", transitional.getSelect().getDefault()); + assertEquals("2", transitional.getSourceRatings().getRatingSpecIds().get(1).getPosition()); + assertEquals("A.Stage;Flow.Linear.Production", transitional.getSourceRatings().getRatingSpecIds().get(1).getValue()); + + // xml -> dto -> json -> dto -> xml is stable + String dbXml = JsonRatingUtils.writeXml(ratings); + String viaJson = JsonRatingUtils.jsonToXml(JsonRatingUtils.writeJson(ratings)); + assertEquals(dbXml, viaJson); + } + + @Test + void test_schema_location_kept_in_json_dropped_in_xml() throws IOException, RatingException { + String xml = loadResourceAsString("cwms/cda/data/dao/ECMT_Stage_Stage_Linear_StepCorrections.xml"); + RatingsDocument ratings = JsonRatingUtils.readXml(xml); + assertEquals("http://www.hec.usace.army.mil/xmlSchema/cwms/Ratings.xsd", + ratings.getNoNamespaceSchemaLocation()); + assertTrue(JsonRatingUtils.writeJson(ratings).contains("noNamespaceSchemaLocation")); + + String dbXml = JsonRatingUtils.writeXml(ratings); + assertFalse(dbXml.contains("noNamespaceSchemaLocation")); + assertNotNull(JsonRatingUtils.toRatingSet(ratings)); + } + + @Test + void test_remove_templates() throws IOException { + String xml = loadResourceAsString("cwms/cda/api/spk/ratings_ind.xml"); + RatingsDocument ratings = JsonRatingUtils.readXml(xml); + RatingsDocument noTemplates = new RatingsDocument.Builder(ratings).withRatingTemplates(null).build(); + + String dbXml = JsonRatingUtils.writeXml(noTemplates); + assertFalse(dbXml.contains("rating-template")); + assertTrue(dbXml.contains("")); + assertTrue(dbXml.contains("")); + } + + @Test + void test_unknown_field_rejected() { + String json = "{\"simple-rating\": {\"office-id\": \"SWT\", \"not-a-field\": \"x\"}}"; + assertThrows(IOException.class, () -> JsonRatingUtils.readJson(json)); } }