package gov.nysenate.openleg.util; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import gov.nysenate.openleg.model.spotcheck.SpotCheckRefType; import gov.nysenate.openleg.model.spotcheck.senatesite.SenateSiteDumpFragment; import gov.nysenate.openleg.model.spotcheck.senatesite.SenateSiteDumpId; import gov.nysenate.openleg.model.spotcheck.senatesite.SenateSiteDumpRangeId; import gov.nysenate.openleg.model.spotcheck.senatesite.SenateSiteDumpSessionId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.IOException; import java.time.LocalDateTime; import java.util.Optional; /** * Parses a {@link SenateSiteDumpFragment} from a json String and {@link SpotCheckRefType}. * The json string has the following mandatory strings: from, to, part, totalParts, session. */ @Service public class SenateSiteDumpFragParser { @Autowired private ObjectMapper objectMapper; /** * <p>Parse a json string into a {@link SenateSiteDumpFragment}. Its SenateSiteDumpId implementation * depends on what information is included in the json. A json with valid values for <code>from</code> * and <code>to</code> will use a {@link SenateSiteDumpRangeId} while a json with a valid <code>session</code> * value will use {@link SenateSiteDumpSessionId}.</p> * <p>Throws <code>SenateSiteDumpFragParserException</code> if a required json value is missing.</p> * @param json The json string to parse. * @param refType The SpotCheckRefType of this json. * @return * @throws IOException * @see SenateSiteDumpId */ public SenateSiteDumpFragment parseFragment(String json, SpotCheckRefType refType) throws IOException { JsonNode rootNode = objectMapper.readValue(json, JsonNode.class); LocalDateTime from = parseDateTimeFromNode(getRequiredNode(rootNode, "from")); LocalDateTime to = parseDateTimeFromNode(getRequiredNode(rootNode, "to")); int part = getRequiredNode(rootNode, "part").asInt(); int totalParts = getRequiredNode(rootNode, "totalParts").asInt(); JsonNode sessionNode = getRequiredNode(rootNode, "session"); Optional<Integer> sessionYearOpt = sessionNode.isNull() ? Optional.empty() : Optional.of(sessionNode.asInt()); SenateSiteDumpId dumpId = createDumpId(from, to, totalParts, sessionYearOpt, refType); return new SenateSiteDumpFragment(dumpId, part); } /** * Get a node from a rootNode and field name. * If the node does not exist, throw exception. */ private JsonNode getRequiredNode(JsonNode rootNode, String fieldName) { if (rootNode.has(fieldName)) { return rootNode.get(fieldName); } else { throw SenateSiteDumpFragParserException.missingField(fieldName); } } /** Return a LocalDateTime parsed from a JsonNode if the node contains text. Otherwise return null. */ private LocalDateTime parseDateTimeFromNode(JsonNode node) { if (node.asText().equals("")) { return null; } return LocalDateTime.parse(node.asText(), DateUtils.PUBLIC_WEBSITE_DUMP_DATETIME_FORMAT); } /** * Returns a specific <code>SenateSiteDumpId</code> implementation depending on data in the json. * @see #parseFragment(String, SpotCheckRefType) */ private SenateSiteDumpId createDumpId(LocalDateTime from, LocalDateTime to, int totalParts, Optional<Integer> sessionYearOpt, SpotCheckRefType refType) { SenateSiteDumpId dumpId = null; if (to == null) { throw SenateSiteDumpFragParserException.nullField("to"); } if (sessionYearOpt.isPresent()) { dumpId = new SenateSiteDumpSessionId(refType, totalParts, sessionYearOpt.get(), to); } else if (from != null) { dumpId = new SenateSiteDumpRangeId(refType, totalParts, from, to); } else { throw new SenateSiteDumpFragParserException( "Invalid senate site dump fragment: both 'from' and 'session' fields are null"); } return dumpId; } /** Exceptions */ public static class SenateSiteDumpFragParserException extends RuntimeException { private static final long serialVersionUID = 5133300734005459879L; private SenateSiteDumpFragParserException(String message) { super(message); } public static SenateSiteDumpFragParserException missingField(String missingField) { return new SenateSiteDumpFragParserException( "Required field: " + missingField + ", is missing from json dump."); } public static SenateSiteDumpFragParserException nullField(String field) { return new SenateSiteDumpFragParserException( "Required field: " + field + " is null"); } } }