Configurations.java
/*******************************************************************************
* Copyright (c) 2023 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.model.util;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.eclipse.rdf4j.common.annotation.InternalUseOnly;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility functions for working with RDF4J Models representing configuration settings.
*
* @author Jeen Broekstra
* @implNote intended for internal use only.
*/
@InternalUseOnly
public class Configurations {
private static final Logger logger = LoggerFactory.getLogger(Configurations.class);
/**
* Verifies if use of legacy configuration vocabulary is preferred. Defaults to <code>false</code>. Can be set by
* having a system property <code>org.eclipse.rdf4j.model.vocabulary.useLegacyConfig</code> set to
* <code>true</code>.
*
* @return <code>true</code> if <code>org.eclipse.rdf4j.model.vocabulary.useLegacyConfig</code> system property is
* set to <code>true</code>, <code>false</code> otherwise.
* @since 5.0.0
*/
public static boolean useLegacyConfig() {
return "true".equalsIgnoreCase(System.getProperty("org.eclipse.rdf4j.model.vocabulary.useLegacyConfig"));
}
/**
* Verifies if the supplied configuration model uses any legacy vocabulary by checking the IRIs of its properties
*
* @param configModel a configuration model
* @return <code>true</code> if any property IRIs start with <code>http://www.openrdf.org/config</code>,
* <code>false</code> otherwise.
*/
public static boolean hasLegacyConfiguration(Model configModel) {
return configModel.predicates()
.stream()
.anyMatch(p -> p.stringValue().startsWith("http://www.openrdf.org/config"));
}
/**
* Retrieve a property value for the supplied subject as a {@link Resource} if present, falling back to a supplied
* legacy property .
* <p>
* This method allows querying repository config models with a mix of old and new namespaces.
*
* @param model the model to retrieve property values from.
* @param subject the subject of the property.
* @param property the property to retrieve the value of.
* @param legacyProperty legacy property to use if the supplied property has no value in the model.
* @return the resource value for supplied subject and property (or the legacy property ), if present.
*/
@InternalUseOnly
public static Optional<Resource> getResourceValue(Model model, Resource subject, IRI property, IRI legacyProperty) {
var preferredProperty = useLegacyConfig() ? legacyProperty : property;
var fallbackProperty = useLegacyConfig() ? property : legacyProperty;
var preferredResult = Models.objectResource(model.getStatements(subject, preferredProperty, null));
var fallbackResult = Models.objectResource(model.getStatements(subject, fallbackProperty, null));
logDiscrepancyWarning(preferredResult, fallbackResult);
if (preferredResult.isPresent()) {
return preferredResult;
}
return fallbackResult;
}
/**
* Retrieve a property value for the supplied subject as a {@link Literal} if present, falling back to a supplied
* legacy property .
* <p>
* This method allows querying repository config models with a mix of old and new namespaces.
*
* @param model the model to retrieve property values from.
* @param subject the subject of the property.
* @param property the property to retrieve the value of.
* @param legacyProperty legacy property to use if the supplied property has no value in the model.
* @return the literal value for supplied subject and property (or the legacy property ), if present.
*/
@InternalUseOnly
public static Optional<Literal> getLiteralValue(Model model, Resource subject, IRI property, IRI legacyProperty) {
var preferredProperty = useLegacyConfig() ? legacyProperty : property;
var fallbackProperty = useLegacyConfig() ? property : legacyProperty;
var preferredResult = Models.objectLiteral(model.getStatements(subject, preferredProperty, null));
var fallbackResult = Models.objectLiteral(model.getStatements(subject, fallbackProperty, null));
logDiscrepancyWarning(preferredResult, fallbackResult);
if (preferredResult.isPresent()) {
return preferredResult;
}
return fallbackResult;
}
/**
* Retrieve a property value for the supplied subject as a {@link Value} if present, falling back to a supplied
* legacy property .
* <p>
* This method allows querying repository config models with a mix of old and new namespaces.
*
* @param model the model to retrieve property values from.
* @param subject the subject of the property.
* @param property the property to retrieve the value of.
* @param legacyProperty legacy property to use if the supplied property has no value in the model.
* @return the literal value for supplied subject and property (or the legacy property ), if present.
*/
@InternalUseOnly
public static Optional<Value> getValue(Model model, Resource subject, IRI property, IRI legacyProperty) {
var preferredProperty = useLegacyConfig() ? legacyProperty : property;
var fallbackProperty = useLegacyConfig() ? property : legacyProperty;
var preferredResult = Models.object(model.getStatements(subject, preferredProperty, null));
var fallbackResult = Models.object(model.getStatements(subject, fallbackProperty, null));
logDiscrepancyWarning(preferredResult, fallbackResult);
if (preferredResult.isPresent()) {
return preferredResult;
}
return fallbackResult;
}
/**
* Retrieve all property values for the supplied subject as a Set of values and include all values for any legacy
* property.
* <p>
* This method allows querying repository config models with a mix of old and new namespaces.
*
* @param model the model to retrieve property values from.
* @param subject the subject of the property.
* @param property the property to retrieve the values of.
* @param legacyProperty legacy property to retrieve values of.
* @return the set of values for supplied subject and property (and/or legacy property).
*/
@InternalUseOnly
public static Set<Value> getPropertyValues(Model model, Resource subject, IRI property, IRI legacyProperty) {
var preferredProperty = useLegacyConfig() ? legacyProperty : property;
var fallbackProperty = useLegacyConfig() ? property : legacyProperty;
var preferredObjects = model.filter(subject, preferredProperty, null).objects();
var fallbackObjects = model.filter(subject, fallbackProperty, null).objects();
if (!fallbackObjects.isEmpty() && !preferredObjects.equals(fallbackObjects)) {
var msg = "Discrepancy between use of the old and new config vocabulary.";
// depending on whether preferred is set, we log on warn or debug
if (preferredObjects.isEmpty()) {
logger.debug(msg);
} else {
logger.warn(msg);
}
if (preferredObjects.containsAll(fallbackObjects)) {
return preferredObjects;
} else if (fallbackObjects.containsAll(preferredObjects)) {
return fallbackObjects;
}
Set<Value> results = new HashSet<>(preferredObjects);
results.addAll(fallbackObjects);
return results;
}
return preferredObjects;
}
/**
* Retrieve a property value for the supplied subject as a {@link IRI} if present, falling back to a supplied legacy
* property .
* <p>
* This method allows querying repository config models with a mix of old and new namespaces.
*
* @param model the model to retrieve property values from.
* @param subject the subject of the property.
* @param property the property to retrieve the value of.
* @param legacyProperty legacy property to use if the supplied property has no value in the model.
* @return the IRI value for supplied subject and property (or the legacy property ), if present.
*/
@InternalUseOnly
public static Optional<IRI> getIRIValue(Model model, Resource subject, IRI property, IRI legacyProperty) {
var preferredProperty = useLegacyConfig() ? legacyProperty : property;
var fallbackProperty = useLegacyConfig() ? property : legacyProperty;
var preferredResult = Models.objectIRI(model.getStatements(subject, preferredProperty, null));
var fallbackResult = Models.objectIRI(model.getStatements(subject, fallbackProperty, null));
logDiscrepancyWarning(preferredResult, fallbackResult);
if (preferredResult.isPresent()) {
return preferredResult;
}
return fallbackResult;
}
/**
* Retrieve the subject of the supplied type, falling back to a supplied legacy type.
*
* @param model the model to retrieve property values from.
* @param type the type to retrieve the value of.
* @param legacyType legacy type to use if the supplied type has no value in the model.
* @return The subject of the supplied type (or the legacy type), if present.
*/
@InternalUseOnly
public static Optional<Resource> getSubjectByType(Model model, IRI type, IRI legacyType) {
var preferredType = useLegacyConfig() ? legacyType : type;
var fallbackType = useLegacyConfig() ? type : legacyType;
var preferredResult = Models.subject(model.getStatements(null, RDF.TYPE, preferredType));
var fallbackResult = Models.subject(model.getStatements(null, RDF.TYPE, fallbackType));
logDiscrepancyWarning(preferredResult, fallbackResult);
if (preferredResult.isPresent()) {
return preferredResult;
}
return fallbackResult;
}
private static void logDiscrepancyWarning(Optional<? extends Value> preferred,
Optional<? extends Value> fallback) {
if (fallback.isPresent() && !preferred.equals(fallback)) {
var msg = "Discrepancy between use of the old and new config vocabulary.";
// depending on whether preferred is set, we log on warn or debug
if (preferred.isEmpty()) {
logger.debug(msg);
} else {
logger.warn(msg);
}
}
}
}