From 3687a6833b6c6b787d015d0f1d262ca1ce972ec6 Mon Sep 17 00:00:00 2001 From: Antonio Garcia-Dominguez Date: Thu, 14 Nov 2024 21:05:47 +0000 Subject: [PATCH] Allow for :: in type and property names --- README.md | 13 +++--- .../epsilon/emc/rdf/RDFQualifiedName.java | 41 ++++++++++++------- .../eclipse/epsilon/emc/rdf/RDFModelTest.java | 16 ++++++++ 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index cc261bc..fec7826 100644 --- a/README.md +++ b/README.md @@ -70,14 +70,15 @@ To obtain a specific RDF resource by its URL: var goblin = Model.getElementById('http://example.org/#green-goblin'); ``` -To list all the resources that have an `rdf:type` predicate with a certain object acting as their type, use `Type.all`: +To list all the resources that have an `rdf:type` predicate with a certain object acting as their type, use `Prefix::Type.all`: ``` -`foaf:Person`.all.println('All people: '); +foaf::Person.all.println('All people: '); ``` -Note the above use of `foaf:Person`, using both the prefix and the local name, and the use of backticks to escape the name. -If there is no risk of ambiguity, you can just use the local name: +You could also use `` `foaf:Person` `` to follow a more RDF-like style, but you would need backticks to escape the name. + +If there is no risk of ambiguity, you can just use the type name: ``` Person.all.println('All people: '); @@ -94,7 +95,7 @@ For example: goblin.`rel:enemyOf`.println('Enemies of the Green Goblin: '); ``` -Note that currently `resource.p` will always return a collection, as we do not leverage yet the RDFS descriptions that could indicate the cardinality of `p`. +If we need to specify a prefix, we can use `` x.`prefix:localName` `` or `` x.`prefix::localName` `` (the EOL grammar requires backticks for colons inside property names, whether it's `:` or `::`). If there is no risk of ambiguity, you can also drop the prefix: @@ -105,6 +106,8 @@ goblin.enemyOf.println('Enemies of the Green Goblin: '); If there are predicates with the same local name but different namespace URIs in the graph formed by all the loaded documents in a model, a warning will be issued. In this case, you should be using a prefix to avoid the ambiguity. +**Note:** currently `resource.p` will always return a collection, as we do not leverage yet the RDFS descriptions that could indicate the cardinality of `p`. + ### Values of predicates The values in `resource.p` will be either other resources, or the values of the associated literals (without filtering by language tags). diff --git a/bundles/org.eclipse.epsilon.emc.rdf/src/org/eclipse/epsilon/emc/rdf/RDFQualifiedName.java b/bundles/org.eclipse.epsilon.emc.rdf/src/org/eclipse/epsilon/emc/rdf/RDFQualifiedName.java index d27bb30..ef022db 100644 --- a/bundles/org.eclipse.epsilon.emc.rdf/src/org/eclipse/epsilon/emc/rdf/RDFQualifiedName.java +++ b/bundles/org.eclipse.epsilon.emc.rdf/src/org/eclipse/epsilon/emc/rdf/RDFQualifiedName.java @@ -14,12 +14,16 @@ import java.util.Map; import java.util.function.Function; +import java.util.regex.Pattern; /** * Represents a partially or fully-qualified name for an RDF resource, with an * optional namespace URI. Instances are created using {@link #from(String, Map)}. */ public class RDFQualifiedName { + + protected static final Pattern COLON_SPLITTER = Pattern.compile(":{1,2}"); + final String prefix; final String namespaceURI; final String localName; @@ -33,30 +37,37 @@ public RDFQualifiedName(String prefix, String nsURI, String localName, String la } /** - * Parses a property name, which may be in the form + * Parses a property name, which matches * {@code (prefix:)?localName(@language)?}. */ public static RDFQualifiedName from(String property, Function prefixToURIMapper) { - int colonIdx = property.indexOf(':'); - String prefix = null, nsURI = null; - if (colonIdx != -1) { - prefix = property.substring(0, colonIdx); - nsURI = prefixToURIMapper.apply(prefix); + String[] parts = COLON_SPLITTER.split(property); + if (parts.length > 1) { + String nsURI = prefixToURIMapper.apply(parts[0]); if (nsURI == null) { - throw new IllegalArgumentException(String.format("Unknown prefix '%s'", prefix)); + throw new IllegalArgumentException(String.format("Unknown prefix '%s'", parts[0])); } - - property = property.substring(colonIdx + 1); + return from(parts[0], nsURI, parts[1]); } - int atIdx = property.indexOf('@'); - String languageTag = null; - if (atIdx != -1) { - languageTag = property.substring(atIdx + 1); - property = property.substring(0, atIdx); + return from(null, null, property); + } + + /** + * Parses a property where the prefix (if any) has already been resolved to + * a namespace URI, and the local name matches {@code localName(@language)?}. + */ + public static RDFQualifiedName from(String prefix, String nsURI, String localNameWithOptionalTag) { + int atIdx = localNameWithOptionalTag.indexOf('@'); + String localName = null, languageTag = null; + if (atIdx == -1) { + localName = localNameWithOptionalTag; + } else { + languageTag = localNameWithOptionalTag.substring(atIdx + 1); + localName = localNameWithOptionalTag.substring(0, atIdx); } - return new RDFQualifiedName(prefix, nsURI, property, languageTag); + return new RDFQualifiedName(prefix, nsURI, localName, languageTag); } public RDFQualifiedName withLocalName(String newLocalName) { diff --git a/tests/org.eclipse.epsilon.emc.rdf.tests/src/org/eclipse/epsilon/emc/rdf/RDFModelTest.java b/tests/org.eclipse.epsilon.emc.rdf.tests/src/org/eclipse/epsilon/emc/rdf/RDFModelTest.java index eaf970d..f3a92b1 100644 --- a/tests/org.eclipse.epsilon.emc.rdf.tests/src/org/eclipse/epsilon/emc/rdf/RDFModelTest.java +++ b/tests/org.eclipse.epsilon.emc.rdf.tests/src/org/eclipse/epsilon/emc/rdf/RDFModelTest.java @@ -88,6 +88,13 @@ public void getNamesWithPrefix() throws Exception { assertEquals(SPIDERMAN_NAMES, names); } + @Test + public void getNamesWithDoubleColonPrefix() throws Exception { + RDFResource res = (RDFResource) model.getElementById(SPIDERMAN_URI); + Set names = new HashSet<>((Collection) pGetter.invoke(res, "foaf::name", context)); + assertEquals(SPIDERMAN_NAMES, names); + } + @Test public void getNameLiteralsWithPrefix() throws Exception { RDFResource res = (RDFResource) model.getElementById(SPIDERMAN_URI); @@ -150,6 +157,15 @@ public void getAllPeopleWithPrefix() throws Exception { assertEquals(ALL_PERSON_URIS, uris); } + @Test + public void getAllPeopleWithPrefixDoubleColon() throws Exception { + Set uris = new HashSet<>(); + for (Object o : model.getAllOfType("foaf::Person")) { + uris.add((String) pGetter.invoke(o, "uri", context)); + } + assertEquals(ALL_PERSON_URIS, uris); + } + @Test public void knownTypes() { assertTrue("The model should confirm that it knows the foaf:Person type", model.hasType("foaf:Person"));