All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.yangtools.yang.common.Revision Maven / Gradle / Ivy

There is a newer version: 14.0.4
Show newest version
/*
 * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.yangtools.yang.common;

import static java.util.Objects.requireNonNull;

import java.io.DataInput;
import java.io.Externalizable;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Optional;
import java.util.regex.Pattern;
import org.checkerframework.checker.regex.qual.Regex;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;

/**
 * Dedicated object identifying a YANG module revision.
 *
 * 

API design note

* This class defines the contents of a revision statement, but modules do not require to have a revision (e.g. they * have not started to keep track of revisions). * *

* APIs which involve this class should always transfer instances via {@code Optional}, which is * the primary bridge data type. Implementations can use nullable fields with explicit conversions to/from * {@link Optional}. Both patterns can take advantage of {@link #compare(Optional, Optional)} and * {@link #compare(Revision, Revision)} respectively. */ public final class Revision implements RevisionUnion { // Note: since we are using writeReplace() this version is not significant. @java.io.Serial private static final long serialVersionUID = 1L; private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Regex // FIXME: we should improve this to filter incorrect dates -- see constructor. private static final String STRING_FORMAT_PATTERN_STR = "\\d\\d\\d\\d\\-\\d\\d-\\d\\d"; /** * String format pattern, which can be used to match parts of a string into components. */ public static final Pattern STRING_FORMAT_PATTERN = Pattern.compile(STRING_FORMAT_PATTERN_STR); /** * Revision which compares as greater than any other valid revision. */ public static final Revision MAX_VALUE = Revision.of("9999-12-31"); private final @NonNull String str; private Revision(final @NonNull String str) { /* * According to RFC7950 (https://www.rfc-editor.org/rfc/rfc7950#section-7.1.9): * * The "revision" statement specifies the editorial revision history of * the module, including the initial revision. A series of "revision" * statements detail the changes in the module's definition. The * argument is a date string in the format "YYYY-MM-DD", [...] * * Hence we use JDK-provided parsing faculties to parse the date. */ FORMATTER.parse(str); this.str = str; } /** * Parse a revision string. * * @param str String to be parsed * @return A Revision instance. * @throws DateTimeParseException if the string format does not conform specification. * @throws NullPointerException if the string is null */ public static @NonNull Revision of(final @NonNull String str) { return new Revision(str); } /** * Parse a (potentially null) revision string. Null strings result result in {@link Optional#empty()}. * * @param str String to be parsed * @return An optional Revision instance. * @throws DateTimeParseException if the string format does not conform specification. */ public static @NonNull Optional ofNullable(final @Nullable String str) { return str == null ? Optional.empty() : Optional.of(new Revision(str)); } @Override public Revision revision() { return this; } @Override public String unionString() { return str; } public static @NonNull Revision readFrom(final DataInput in) throws IOException { return ofRead(in.readUTF()); } static @NonNull Revision ofRead(final @NonNull String str) throws IOException { try { return of(str); } catch (DateTimeParseException e) { throw new IOException("Invalid revision-date string", e); } } /** * Compare two {@link Optional}s wrapping Revisions. Arguments and return value are consistent with * {@link java.util.Comparator#compare(Object, Object)} interface contract. Missing revisions compare as lower * than any other revision. * * @param first First optional revision * @param second Second optional revision * @return Positive, zero, or negative integer. */ public static int compare(final @NonNull Optional first, final @NonNull Optional second) { if (first.isPresent()) { return second.isPresent() ? first.orElseThrow().compareTo(second.orElseThrow()) : 1; } return second.isPresent() ? -1 : 0; } /** * Compare two explicitly nullable Revisions. Unlike {@link #compareTo(Revision)}, this handles both arguments * being null such that total ordering is defined. * * @param first First revision * @param second Second revision * @return Positive, zero, or negative integer. */ public static int compare(final @Nullable Revision first, final @Nullable Revision second) { if (first != null) { return second != null ? first.compareTo(second) : 1; } return second != null ? -1 : 0; } @Override public int hashCode() { return str.hashCode(); } @Override public boolean equals(final Object obj) { return this == obj || obj instanceof Revision other && str.equals(other.str); } /** * Return the revision string according to * RFC7950, section 7.1.9. The string is * guaranteed to be composed on 10 ASCII characters in {@code YYYY-MM-DD} format. The items are guaranteed to be * decimal numbers. There are no further guarantees as to convertibility to an actual calendar date, as this method * can validly return {@code "2019-02-29"} (even if 2019 is not a leap year) or {@code "2022-12-32"} (even if no * month has 32 days). * * @return A string in {@code YYYY-MM-DD} format */ @Override public String toString() { return str; } @java.io.Serial Object writeReplace() { return new RUv1(str); } @java.io.Serial private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { throwNSE(); } @java.io.Serial private void readObjectNoData() throws ObjectStreamException { throwNSE(); } @java.io.Serial private void writeObject(final ObjectOutputStream stream) throws IOException { throwNSE(); } static void throwNSE() throws NotSerializableException { throw new NotSerializableException(Revision.class.getName()); } @Deprecated(since = "12.0.0", forRemoval = true) private static final class Proxy implements Externalizable { @java.io.Serial private static final long serialVersionUID = 1L; private Revision revision; @SuppressWarnings("checkstyle:redundantModifier") public Proxy() { // For Externalizable } @Override public void writeExternal(final ObjectOutput out) throws IOException { throw new NotSerializableException(Proxy.class.getName()); } @Override public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { revision = Revision.of((String) in.readObject()); } @java.io.Serial private Object readResolve() { return requireNonNull(revision); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy