Skip to content

Commit

Permalink
fix (core): Thing get(String.class) now works for Set/List Collections
Browse files Browse the repository at this point in the history
  • Loading branch information
vorburger committed Sep 29, 2024
1 parent 031c434 commit 2432fdf
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 76 deletions.
3 changes: 1 addition & 2 deletions java/dev/enola/thing/Literal.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
*
* <p>This is a LEGACY type which (now) overlaps with {@link Thing#datatype(String predicateIRI)}
* (and a String value property). Prefer using that than this, which will eventually be removed.
* Until it's removed, use {@link Thing#datatypeLEGACY(String)} to obtain a property's datatype.
*/
@Deprecated // TODO Remove this; see above
public final record Literal(String value, String datatypeIRI) {}
public record Literal(String value, String datatypeIRI) {}
103 changes: 103 additions & 0 deletions java/dev/enola/thing/ObjectConversions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2024 The Enola <https://enola.dev> Authors
*
* 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
*
* https://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 dev.enola.thing;

import com.google.common.collect.Collections2;

import dev.enola.common.context.TLC;
import dev.enola.common.convert.ConversionException;
import dev.enola.datatype.DatatypeRepository;

import org.jspecify.annotations.Nullable;

import java.io.IOException;
import java.net.URI;
import java.util.Collection;
import java.util.Optional;

// package-local
class ObjectConversions {

static <T> Optional<T> as(
Object object,
Class<T> klass,
PredicatesObjects predicatesObjects,
String predicateIRI) {
if (object == null) return Optional.empty();
if (klass.isInstance(object)) return Optional.of((T) object);
if (String.class.equals(klass)) {
if (object instanceof Literal literal) return Optional.of((T) literal.value());
if (object instanceof URI uri) return Optional.of((T) uri.toString());
if (object instanceof Link link) return Optional.of((T) link.iri());
if (object instanceof Collection<?> collection)
return Optional.of(
(T) Collections2.transform(collection, e -> e.toString()).toString());
// TODO Ideally, it should look up the "right" text, using a Lang Ctx Key from the TLC
if (object instanceof LangString langString) return Optional.of((T) langString.text());
}
try {
var dtIRI = datatypeLEGACY(predicateIRI, predicatesObjects);
// TODO Find Datatype via object Java class lookup in DatatypeRepository?
if (dtIRI == null)
throw new IllegalStateException(
predicateIRI
+ " has no Datatype; cannot convert "
+ object
+ " of "
+ object.getClass()
+ " to "
+ klass);
var dtr = TLC.get(DatatypeRepository.class);
var dt = dtr.get(dtIRI);
if (dt == null)
throw new IllegalStateException(
dtIRI
+ " not found; cannot convert "
+ object
+ " of "
+ object.getClass()
+ " to "
+ klass);
// TODO Make this more generic so it can support klass other than String
var opt = dt.stringConverter().convertObjectToType(object, klass);
if (opt.isEmpty())
throw new IllegalStateException(
object + " of " + object.getClass() + " to " + klass);
return opt;

} catch (IOException e) {
// TODO Get rid of throws IOException and remove this.
// Or better log any exceptions and return just Optional.empty()?
throw new ConversionException("Failed to convert " + object + " to " + klass, e);
}
}

@Deprecated // TODO Remove once record Literal is gone
private static @Nullable String datatypeLEGACY(
String predicateIRI, PredicatesObjects predicatesObjects) {
var datatype = predicatesObjects.datatype(predicateIRI);
if (datatype != null) return datatype;

var object = predicatesObjects.get(predicateIRI);
if (object != null && object instanceof Literal literal) {
return literal.datatypeIRI();
}

return null;
}
}
66 changes: 3 additions & 63 deletions java/dev/enola/thing/PredicatesObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,15 @@
import com.google.common.reflect.TypeToken;
import com.google.errorprone.annotations.ImmutableTypeParameter;

import dev.enola.common.context.TLC;
import dev.enola.common.convert.ConversionException;
import dev.enola.datatype.DatatypeRepository;
import dev.enola.thing.repo.ThingProvider;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import org.jspecify.annotations.Nullable;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.*;

/**
* PredicatesObjects is a "struct" of Predicates and their Objects.
Expand Down Expand Up @@ -94,19 +89,6 @@ default boolean isStruct(String predicateIRI) {
@Deprecated // TODO Is this really useful? In which use case scenario? Remove...
Map<String, String> datatypes();

@Deprecated // TODO Remove once record Literal is gone
default @Nullable String datatypeLEGACY(String predicateIRI) {
var datatype = datatype(predicateIRI);
if (datatype != null) return datatype;

var object = get(predicateIRI);
if (object != null && object instanceof Literal literal) {
return literal.datatypeIRI();
}

return null;
}

/**
* Object of predicate. The type is e.g. directly a String, Integer etc. Alternatively, it may
* be a {@link Link} (or {@link URI}) with an IRI or another PredicatesObjects (for an "inline
Expand All @@ -115,7 +97,7 @@ default boolean isStruct(String predicateIRI) {
*
* @deprecated Use {@link #get(String, Class)} instead.
*/
@Deprecated // TODO Remove after replacing all usages with #get(String, Class)
@Deprecated // TODO Remove after replacing all usages with #get(String, aClass | Object.class)
<T> @Nullable T get(String predicateIRI);

/**
Expand Down Expand Up @@ -150,49 +132,7 @@ default <T> Optional<T> getOptional(String predicateIRI, TypeToken<T> typeToken)
@SuppressWarnings("unchecked")
default <T> Optional<T> getOptional(String predicateIRI, Class<T> klass) {
Object object = get(predicateIRI);
if (object == null) return Optional.empty();
if (klass.isInstance(object)) return Optional.of((T) object);
if (String.class.equals(klass)) {
if (object instanceof Literal literal) return Optional.of((T) literal.value());
if (object instanceof URI uri) return Optional.of((T) uri.toString());
if (object instanceof Link link) return Optional.of((T) link.iri());
// TODO Ideally, it should look up the "right" text, using a Lang Ctx Key from the TLC
if (object instanceof LangString langString) return Optional.of((T) langString.text());
}
try {
var dtIRI = datatypeLEGACY(predicateIRI);
// TODO Find Datatype via object Java class lookup in DatatypeRepository?
if (dtIRI == null)
throw new IllegalStateException(
predicateIRI
+ " has no Datatype; cannot convert "
+ object
+ " of "
+ object.getClass()
+ " to "
+ klass);
var dtr = TLC.get(DatatypeRepository.class);
var dt = dtr.get(dtIRI);
if (dt == null)
throw new IllegalStateException(
dtIRI
+ " not found; cannot convert "
+ object
+ " of "
+ object.getClass()
+ " to "
+ klass);
var opt = dt.stringConverter().convertObjectToType(object, klass);
if (opt.isEmpty())
throw new IllegalStateException(
object + " of " + object.getClass() + " to " + klass);
return opt;

} catch (IOException e) {
// TODO Get rid of throws IOException and remove this.
// Or better log any exceptions and return just Optional.empty()?
throw new ConversionException("Failed to convert " + object + " to " + klass, e);
}
return ObjectConversions.as(object, klass, this, predicateIRI);
}

default @Nullable String getString(String predicateIRI) {
Expand Down
8 changes: 8 additions & 0 deletions java/dev/enola/thing/impl/ImmutableThingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public void testToString() {
assertThat(testThingToString).contains("hello");
}

@Test
public void getAllAsString() {
var testThing = ThingRepositoriesTest.TEST_THING;
for (var predicateIRI : testThing.predicateIRIs()) {
assertThat(testThing.get(predicateIRI, String.class)).isNotEmpty();
}
}

@Test
public void message() {
var testThing = ThingRepositoriesTest.TEST_THING;
Expand Down
7 changes: 0 additions & 7 deletions java/dev/enola/thing/java/DelegatingThing.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,6 @@ public String datatype(String predicateIRI) {
return delegate.datatype(predicateIRI);
}

@Override
@Deprecated
@Nullable
public String datatypeLEGACY(String predicateIRI) {
return delegate.datatypeLEGACY(predicateIRI);
}

@Override
public <T> @Nullable T get(String predicateIRI) {
return delegate.get(predicateIRI);
Expand Down
7 changes: 7 additions & 0 deletions java/dev/enola/thing/repo/ThingRepositoriesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import static org.junit.Assert.assertThrows;

import com.google.common.collect.ImmutableList;

import dev.enola.data.Store;
import dev.enola.thing.*;
import dev.enola.thing.impl.ImmutableThing;
Expand All @@ -37,6 +39,11 @@ public class ThingRepositoriesTest {
.set("http://example.com/link", new Link("http://example.com"))
.set("http://example.com/mls", new LangString("Saluton", "eo"))
.set("http://example.com/lit", new Literal("k&ç#'", "test:type"))
.set(
"http://example.com/list",
ImmutableList.of(
new Link("http://example.com"),
new Literal("k&ç#'", "test:type")))
.build();

private void checkStore(Store<?, Thing> thingStore) {
Expand Down
8 changes: 4 additions & 4 deletions models/enola.dev/namespaces.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ enola:Namespaces a rdfs:Class;
enola:namespaces a enola:Namespaces;
schema:description "Active namespace prefixes.";
# TODO enola:doc <https://docs.enola.dev/concepts/namespaces/>
# rdfs:seeAlso <https://lov.linkeddata.es/dataset/lov/>,
# <https://prefix.cc>,
# <https://www.w3.org/2011/rdfa-context/rdfa-1.1>,
# <https://prefix.zazuko.com/prefixes>;
rdfs:seeAlso <https://lov.linkeddata.es/dataset/lov/>,
<https://prefix.cc>,
<https://www.w3.org/2011/rdfa-context/rdfa-1.1>,
<https://prefix.zazuko.com/prefixes>;

<http://ns.adobe.com/pdf/1.3/> "pdf";
<http://purl.org/dc/elements/1.1/> "dc";
Expand Down

0 comments on commit 2432fdf

Please sign in to comment.