001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.io.resource;
019
020import dev.enola.common.ByteSeq;
021
022/**
023 * Change Token ("Tag") for {@link ReadableResource#changeToken()}.
024 *
025 * <p>This is named a "change token" and not "version" (or "revision") because that might imply
026 * something “numeric” - but this is explicitly NOT intended to be used for “is it newer or older”
027 * comparison, only “has it changed”. It's also not called a "fingerprint" because that might imply
028 * it's derived only from the content itself (like a hash), whereas this may be based on more than
029 * that, e.g. additional metadata.
030 */
031// TODO Should implementations also hold and compare the ReadableResource? Or even just its IRI?
032public interface ChangeToken { // skipcq: JAVA-E1041
033
034    /**
035     * Check is this ChangeToken is different from that other ChangeToken.
036     *
037     * <p>If it's not sure, prefer "erring on the side of caution" by returning true instead of
038     * false. That's why two {@link #NOT_AVAILABLE} are considered representing (possibly) different
039     * resource contents (returns true).
040     */
041    boolean isDifferent(ChangeToken other);
042
043    /**
044     * String representation of this ChangeToken, (only) for {@link
045     * ReadableResource#isDifferent(String)}.
046     *
047     * <p>Do not interpret the content of this String. It's intended to be used completely "opaque",
048     * and only for before &amp; after comparison, on a Resource from the same URI. Implementations
049     * are encouraged to return strings which do not (directly) "look like something familiar", to
050     * avoid users relying on implementation details.
051     */
052    String toString();
053
054    ByteSeq toBytes();
055
056    /**
057     * Constant (singleton) for "not available" change tokens.
058     *
059     * <p>Returned by {@link ReadableResource#changeToken()} when it's impossible to obtain a {@link
060     * ChangeToken} e.g. due to internal technical errors, including because the URI points to a
061     * non-existing resource. Its {@link #isDifferent(ChangeToken)} always returns true.
062     */
063    ChangeToken NOT_AVAILABLE =
064            new ChangeToken() {
065                @Override
066                public boolean isDifferent(ChangeToken other) {
067                    return true;
068                }
069
070                @Override
071                public String toString() {
072                    return "N/A";
073                }
074
075                @Override
076                public ByteSeq toBytes() {
077                    return ByteSeq.EMPTY;
078                }
079
080                @Override
081                public boolean equals(Object obj) {
082                    return obj == NOT_AVAILABLE;
083                }
084
085                @Override
086                public int hashCode() {
087                    return System.identityHashCode(this);
088                }
089            };
090}