001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2024-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.thing.metadata;
019
020import dev.enola.common.time.Interval;
021import dev.enola.thing.Thing;
022
023import java.time.Instant;
024import java.util.List;
025
026/**
027 * Provider of _"temporal"_ <a href="https://en.wikipedia.org/wiki/Time">Time</a> related metadata
028 * about Things.
029 */
030public class ThingTimeProvider {
031
032    /**
033     * Provides the (non-overlapping) {@link Interval}s (plural!) during which the Thing at this IRI
034     * "exists".
035     *
036     * <p>This always returns at least 1 Interval, never none (or null) - but it may well be the
037     * {@link Interval#ALL} if the Thing has no temporal metadata.
038     */
039    public Iterable<Interval> existance(Thing thing) {
040
041        // TODO The following is intentionally hard-coded initially, but the thinking is that
042        // eventually the IRIs of these properties should be inferred e.g. using Enola Meta schema
043        // rdfs:subPropertyOf ...
044
045        // Note that the orders, for both start & end, are intentionally by priority we're
046        // considering; i.e. if something has a :timestamp, that's considered before a :createdAt;
047        // but a :modifiedAt is considered only AFTER we don't find e.g. an :endedAt.
048
049        var timestamp = "https://enola.dev/timestamp";
050        var created = "https://enola.dev/createdAt";
051        var started = "https://enola.dev/startedAt";
052        var fileCreated = "https://enola.dev/files/Node/createdAt";
053        var start =
054                thing.getOptional(timestamp, Instant.class)
055                        .or(() -> thing.getOptional(created, Instant.class))
056                        .or(() -> thing.getOptional(started, Instant.class))
057                        .or(() -> thing.getOptional(fileCreated, Instant.class))
058                        .orElse(Instant.MIN);
059
060        var deleted = "https://enola.dev/deletedAt";
061        var ended = "https://enola.dev/endedAt";
062        var fileDeleted = "https://enola.dev/files/Node/deletedAt";
063        var modified = "https://enola.dev/modifiedAt";
064        var end =
065                thing.getOptional(deleted, Instant.class)
066                        .or(() -> thing.getOptional(ended, Instant.class))
067                        .or(() -> thing.getOptional(fileDeleted, Instant.class))
068                        .or(() -> thing.getOptional(modified, Instant.class))
069                        .orElse(Instant.MAX);
070
071        return List.of(Interval.of(start, end));
072    }
073}