From 3ff3e205e8a7eb8557b517d14df5304dcd75bf8c Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Fri, 6 Sep 2024 19:05:46 +0200 Subject: [PATCH] feat (core): Introduce https://enola.dev/labelProperty --- docs/concepts/metadata.md | 3 +++ java/dev/enola/thing/BUILD | 1 + java/dev/enola/thing/KIRI.java | 1 + .../thing/metadata/ThingMetadataProvider.java | 25 +++++++++++++------ .../metadata/ThingMetadataProviderTest.java | 17 +++++++++++++ models/enola.dev/enola.ttl | 6 +++++ test/metadata-label-property.ttl | 2 +- 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/docs/concepts/metadata.md b/docs/concepts/metadata.md index 96e82f32d..c11886265 100644 --- a/docs/concepts/metadata.md +++ b/docs/concepts/metadata.md @@ -41,6 +41,9 @@ This is read from a number of "typical" properties; in order of priority: If the thing itself doesn't have any of these, it will check if its [RDFS range](../models/www.w3.org/2000/01/rdf-schema/range.md) has any. +Alternatively, the (RDFS) `Class` of a Thing's (RDF) `type` can specify an [https://enola.dev/labelProperty](../models/enola.dev/labelProperty.md) +to specify the IRI of a "custom" property to use a label, if present; see `test/metadata-label-property.ttl` for an example illustrating how to use this. + This is always available; if a Thing has none of the above, then it will fallback to the CURIE, or else just the last part of its IRI. ## Description diff --git a/java/dev/enola/thing/BUILD b/java/dev/enola/thing/BUILD index 7bf01b9d1..8a89be93f 100644 --- a/java/dev/enola/thing/BUILD +++ b/java/dev/enola/thing/BUILD @@ -116,6 +116,7 @@ junit_tests( ":thing_java", ":thing_java_proto", "//java/dev/enola/common/context", + "//java/dev/enola/common/context/testlib", "//java/dev/enola/common/convert", "//java/dev/enola/common/function", "//java/dev/enola/common/io", diff --git a/java/dev/enola/thing/KIRI.java b/java/dev/enola/thing/KIRI.java index 777291c4e..ee3684863 100644 --- a/java/dev/enola/thing/KIRI.java +++ b/java/dev/enola/thing/KIRI.java @@ -66,6 +66,7 @@ public static final class E { public static final String IRI_TEMPLATE_PROPERTY = NS + "iriTemplate"; public static final String IRI_TEMPLATE_DATATYPE = NS + "IRITemplate"; + public static final String LABEL_PROPERTY = NS + "labelProperty"; } /** Schema.org Properties. */ diff --git a/java/dev/enola/thing/metadata/ThingMetadataProvider.java b/java/dev/enola/thing/metadata/ThingMetadataProvider.java index edbcaecf7..4971dedfa 100644 --- a/java/dev/enola/thing/metadata/ThingMetadataProvider.java +++ b/java/dev/enola/thing/metadata/ThingMetadataProvider.java @@ -37,7 +37,8 @@ /** * {@link MetadataProvider} implementation based on looking at {@link Things}s obtained via {@link - * ThingProvider}. + * ThingProvider}; see also related getLabelViaProperty(thing, type)); if (label != null) return label; - label = getAlternative(thing, KIRI.RDFS.RANGE, thingX -> getLabel_(thingX)); + label = getLabel_(thing); + if (label != null) return label; + + label = getAlternative(thing, KIRI.RDFS.RANGE, range -> getLabel_(range)); if (label != null) return label; if (!curie.equals(fallbackIRI)) return curie; @@ -123,6 +127,13 @@ private String getLabel(@Nullable Thing thing, String curie, String fallbackIRI) } } + private @Nullable String getLabelViaProperty(@Nullable Thing thing, Thing type) { + if (thing == null) return null; + var typesLabelProperty = type.getString(KIRI.E.LABEL_PROPERTY); + if (typesLabelProperty == null) return null; + return thing.getString(typesLabelProperty); + } + private @Nullable String getLabel_(@Nullable Thing thing) { var label = getString(thing, KIRI.RDFS.LABEL); if (label != null) return label; @@ -184,18 +195,18 @@ private String getImageHTML(Thing thing) { var emoji = getString(thing, KIRI.E.EMOJI); if (emoji != null) return emoji; - emoji = getAlternative(thing, KIRI.RDFS.RANGE, thingX -> getEmoji_(thingX)); + emoji = getAlternative(thing, KIRI.RDFS.RANGE, range -> getEmoji_(range)); if (emoji != null) return emoji; - emoji = getAlternative(thing, KIRI.RDF.TYPE, thingX -> getEmoji_(thingX)); + emoji = getAlternative(thing, KIRI.RDF.TYPE, type -> getEmoji_(type)); return emoji; } private @Nullable String getImageURL_(Thing thing) { - var imageURL = getAlternative(thing, KIRI.RDFS.RANGE, thingX -> getImageURL__(thingX)); + var imageURL = getAlternative(thing, KIRI.RDFS.RANGE, range -> getImageURL__(range)); if (imageURL != null) return imageURL; - imageURL = getAlternative(thing, KIRI.RDF.TYPE, thingX -> getImageURL__(thingX)); + imageURL = getAlternative(thing, KIRI.RDF.TYPE, type -> getImageURL__(type)); return imageURL; } diff --git a/java/dev/enola/thing/metadata/ThingMetadataProviderTest.java b/java/dev/enola/thing/metadata/ThingMetadataProviderTest.java index cd0691969..5162e0661 100644 --- a/java/dev/enola/thing/metadata/ThingMetadataProviderTest.java +++ b/java/dev/enola/thing/metadata/ThingMetadataProviderTest.java @@ -19,18 +19,25 @@ import static com.google.common.truth.Truth.assertThat; +import dev.enola.common.context.testlib.EnolaTestTLCRules; +import dev.enola.common.context.testlib.TestTLCRule; import dev.enola.common.io.iri.namespace.NamespaceConverter; import dev.enola.common.io.iri.namespace.NamespaceConverterIdentity; import dev.enola.thing.KIRI; import dev.enola.thing.Thing; import dev.enola.thing.impl.ImmutableThing; +import dev.enola.thing.io.Loader; +import dev.enola.thing.repo.ThingMemoryRepositoryRW; import dev.enola.thing.repo.ThingProvider; import org.jspecify.annotations.Nullable; +import org.junit.Rule; import org.junit.Test; public class ThingMetadataProviderTest { + @Rule public TestTLCRule rlcRule = EnolaTestTLCRules.BASIC; + private static final NamespaceConverter NONS = new NamespaceConverterIdentity(); private static final ThingProvider NO_THING_PROVIDER = iri -> null; @@ -80,6 +87,16 @@ public void label() { .isEqualTo(THING_LABEL); } + @Test + public void labelViaAlternativeLabelProperty() { + var uri = java.net.URI.create("classpath:/metadata-label-property.ttl"); + var repo = new ThingMemoryRepositoryRW(); + var things = new Loader().load(uri, repo); + var metadataProvider = new ThingMetadataProvider(repo, NONS); + var metadata = metadataProvider.get("https://example.org/test-metadata-label-property"); + assertThat(metadata.label()).isEqualTo("LABEL"); + } + @Test public void description() { assertThat(new ThingMetadataProvider(test, NONS).get(THING_IRI).descriptionHTML()) diff --git a/models/enola.dev/enola.ttl b/models/enola.dev/enola.ttl index 52787c2d9..27064404f 100644 --- a/models/enola.dev/enola.ttl +++ b/models/enola.dev/enola.ttl @@ -85,6 +85,12 @@ enola:emoji a rdf:Property; schema:description "Emoji 😃 of a Thing, from Unicode or Nerdfonts."; enola:emoji "😃". +enola:labelProperty a rdf:Property; + schema:name "Label Property"; + rdfs:domain rdfs:Class; + rdfs:range xsd:anyURI; + schema:description "The IRI of a property to be used as Label for Things of this Class; see https://docs.enola.dev/concepts/metadata/". + enola:parent a rdf:Property; rdfs:range schema:Thing; rdfs:comment "A 'hierarchical' parent. This is typically subclassed - and thus there could be several different kinds of parents. Similar to https://schema.org/isPartOf; but that's specific to CreativeWork, while this is not.". diff --git a/test/metadata-label-property.ttl b/test/metadata-label-property.ttl index 12f2212a7..231572083 100644 --- a/test/metadata-label-property.ttl +++ b/test/metadata-label-property.ttl @@ -19,7 +19,7 @@ @prefix rdfs: . example:test-metadata-label-property-class a rdfs:Class; - enola:label-property enola:altlab. + enola:labelProperty enola:altlab. example:test-metadata-label-property a example:test-metadata-label-property-class; enola:altlab "LABEL".