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}