EntryInfoUtil.java

/*
 * Copyright (c) 2007-2026 MetaSolutions AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.entrystore.rest.springboot.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.entrystore.impl.RepositoryProperties;

import java.net.URI;
import java.util.Set;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class EntryInfoUtil {

	private static final Set<IRI> ENTRY_PREDICATES = Set.of(RDF.TYPE, RepositoryProperties.resource,
			RepositoryProperties.metadata, RepositoryProperties.externalMetadata,
			RepositoryProperties.cachedExternalMetadata, RepositoryProperties.relation, RepositoryProperties.Created,
			RepositoryProperties.Modified);

	private static final Set<IRI> RESOURCE_PREDICATES = Set.of(RDF.TYPE, RepositoryProperties.homeContext);

	/**
	 * Returns the statements of an entry information graph that are shown to a caller who may read neither the
	 * entry's metadata nor its resource: what clients need to resolve, type and date the entry, and the home context
	 * of a user or group. This is an allow-list, so everything else stays hidden, such as the creator, contributors,
	 * ACL, file name, format and size, and any predicate added later.
	 *
	 * @param cachedExternalMetadataURI the subject of {@code es:cached}, or null for an entry without one
	 */
	public static Model reduceForNonReader(Model graph, URI entryURI, URI resourceURI, URI cachedExternalMetadataURI) {
		IRI entry = toIRI(entryURI);
		IRI resource = toIRI(resourceURI);
		IRI cachedExternalMetadata = cachedExternalMetadataURI != null ? toIRI(cachedExternalMetadataURI) : null;
		Model reduced = new LinkedHashModel(graph.getNamespaces());
		for (Statement statement : graph) {
			if (isKept(statement, entry, resource, cachedExternalMetadata)) {
				reduced.add(statement);
			}
		}
		return reduced;
	}

	private static boolean isKept(Statement statement, IRI entry, IRI resource, IRI cachedExternalMetadata) {
		Resource subject = statement.getSubject();
		IRI predicate = statement.getPredicate();
		return (subject.equals(entry) && ENTRY_PREDICATES.contains(predicate))
				|| (subject.equals(resource) && RESOURCE_PREDICATES.contains(predicate))
				|| (subject.equals(cachedExternalMetadata) && predicate.equals(RepositoryProperties.cached));
	}

	private static IRI toIRI(URI uri) {
		return SimpleValueFactory.getInstance().createIRI(uri.toString());
	}
}