Skip to content

Commit

Permalink
feat (core): Introduce ResourceIntoThingConverters
Browse files Browse the repository at this point in the history
  • Loading branch information
vorburger committed Jun 30, 2024
1 parent 458b9ca commit dc4d0d5
Show file tree
Hide file tree
Showing 16 changed files with 253 additions and 13 deletions.
4 changes: 1 addition & 3 deletions java/dev/enola/cli/CommandWithModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import dev.enola.datatype.DatatypeRepositoryBuilder;
import dev.enola.rdf.RdfResourceIntoThingConverter;
import dev.enola.thing.ThingMetadataProvider;
import dev.enola.thing.impl.ImmutableThing;
import dev.enola.thing.io.Loader;
import dev.enola.thing.io.ResourceIntoThingConverter;
import dev.enola.thing.message.ThingProviderAdapter;
Expand Down Expand Up @@ -91,8 +90,7 @@ public final void run() throws Exception {
// TODO Replace DatatypeRepository with store itself, once a Datatype is a Thing
DatatypeRepository dtr = new DatatypeRepositoryBuilder().build();
ThingMemoryRepositoryROBuilder store = new ThingMemoryRepositoryROBuilder();
ResourceIntoThingConverter ritc =
new RdfResourceIntoThingConverter(dtr, ImmutableThing::builder);
ResourceIntoThingConverter ritc = new RdfResourceIntoThingConverter(dtr);
var loader = new Loader(ritc);
var fgrp = new GlobResourceProviders();
for (var globIRI : group.load) {
Expand Down
31 changes: 31 additions & 0 deletions java/dev/enola/common/collect/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright 2023 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.

load("@rules_java//java:defs.bzl", "java_library")

java_library(
name = "collect",
srcs = glob(
["*.java"],
exclude = ["*Test.java"],
),
visibility = ["//:__subpackages__"],
deps = [
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.enola.common;
package dev.enola.common.collect;

import java.util.Collection;
import java.util.OptionalInt;

// TODO Move this into package dev.enola.common.collect
public final class MoreIterables {

/**
Expand Down
29 changes: 29 additions & 0 deletions java/dev/enola/common/collect/MoreIterators.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.common.collect;

import java.util.Iterator;

public final class MoreIterators {

public static <T> Iterable<T> toIterable(Iterator<T> iterator) {
return () -> iterator;
}

private MoreIterators() {}
}
21 changes: 21 additions & 0 deletions java/dev/enola/common/collect/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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.
*/
@NullMarked
package dev.enola.common.collect;

import org.jspecify.annotations.NullMarked;
32 changes: 29 additions & 3 deletions java/dev/enola/common/context/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ public Context() {
* implementations; in practice, at least the key often IS actually simply a {@link String} (but
* it technically does not necessarily have to be).
*
* @param key Key, which must implement {@link #equals(Object)} correctly.
* @param key Key, which must implement {@link #equals(Object)} correctly. By convention, often
* a String formatted like <tt>getClass().getName() + "#METHOD/PARAMETER"</tt>.
* @param value Value to associate with the key.
* @return this, for chaining.
*/
Expand All @@ -68,7 +69,7 @@ public Context push(Object key, Object value) {
return this;
}

/** Get the value for the given key from this or its parent context. */
/** Get the value for the given key, from this or its parent context. May be wnull. */
public @Nullable Object get(Object key) {
check();
var current = last;
Expand All @@ -82,6 +83,31 @@ public Context push(Object key, Object value) {
else return null;
}

public <T> Context push(Class<T> key, T value) {
push(key, (Object) value); // NOT .getName()
return this;
}

/**
* Gets the instance of Class, from this or its parent context. Never null, may throw
* IllegalStateException.
*/
@SuppressWarnings("unchecked")
public <T> T get(Class<T> klass) {
var key = klass; // NOT .getName();
if (isEmpty()) throw new IllegalStateException("Context is empty");
var object = get((Object) key);
if (object == null)
throw new IllegalStateException("Context has no " + key + "; only:\n" + toString(" "));
if (klass.isInstance(object)) return (T) object;
throw new IllegalStateException(
"Context's " + key + " is a " + object.getClass() + " instead of " + klass);
}

private boolean isEmpty() {
return last == null && parent == null;
}

// Nota bene: This (kind of) Stack-like data structure (intentionally)
// does not have (need) any pop() ("goes the weasel”) kind of method!

Expand All @@ -102,7 +128,7 @@ void append(Appendable a, String indent) {
}
}

String toString(String indent) {
private String toString(String indent) {
var sb = new StringBuilder();
append(sb, indent);
return sb.toString();
Expand Down
13 changes: 11 additions & 2 deletions java/dev/enola/common/context/TLC.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,20 @@ public static Context open() {

/** See {@link dev.enola.common.context.Context#get(java.lang.Object). */
public static @Nullable Object get(Object key) {
return context().get(key);
}

/** See {@link dev.enola.common.context.Context#get(java.lang.Class). */
public static <T> T get(Class<T> klass) {
return context().get(klass);
}

private static Context context() {
var tlc = threadLocalContext.get();
if (tlc == null) {
throw new IllegalStateException("open() first!");
throw new IllegalStateException("Missing TLC.open() in call chain!");
}
return tlc.get(key);
return tlc;
}

/* package-local, always keep; never make public! */
Expand Down
1 change: 1 addition & 0 deletions java/dev/enola/common/convert/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ java_library(
visibility = ["//:__subpackages__"],
deps = [
"//java/dev/enola/common",
"//java/dev/enola/common/context",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
Expand Down
3 changes: 2 additions & 1 deletion java/dev/enola/common/convert/ConversionException.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
package dev.enola.common.convert;

import dev.enola.common.URILineColumnMessage;
import dev.enola.common.context.ContextualizedRuntimeException;

import java.net.URI;

/**
* Failures encountered by {@link ConverterInto}, {@link Converter} and {@link BiConverter}
* implementations.
*/
public class ConversionException extends RuntimeException {
public class ConversionException extends ContextualizedRuntimeException {
// TODO Re-try making this extends Exception instead Runtime, now that I have 👟 MoreStreams

public ConversionException(String message, URI uri, long line, long column, Throwable cause) {
Expand Down
6 changes: 6 additions & 0 deletions java/dev/enola/common/function/MoreStreams.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@
/** Static utility methods related to {@code Stream} instances. {@link Streams} has more. */
public final class MoreStreams {

// TODO Move to package dev.enola.common.collect (but must figure out Sneaker relationship)

// TODO Eventually adopting one of (but which?) real reactive frameworks in Enola overall
// and rm this may be better? (That would likely be better than doing something such as e.g.
// https://stackoverflow.com/questions/30117134/aggregate-runtime-exceptions-in-java-8-streams)

public static <T> Iterable<T> toIterable(Stream<T> stream) {
return stream::iterator;
}

// While waiting for e.g. something like https://bugs.openjdk.org/browse/JDK-8148917
public static <T, E extends Exception> void forEach(
Stream<T> stream, CheckedConsumer<T, E> action) throws E {
Expand Down
3 changes: 3 additions & 0 deletions java/dev/enola/rdf/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ java_library(
plugins = ["//tools/bazel/java_plugin:autoservice"],
visibility = ["//:__subpackages__"],
deps = [
"//java/dev/enola/common/context",
"//java/dev/enola/common/convert",
"//java/dev/enola/common/io",
"//java/dev/enola/common/protobuf",
Expand Down Expand Up @@ -58,11 +59,13 @@ junit_tests(
srcs_utils = glob(["*Subject.java"]),
deps = [
":rdf",
"//java/dev/enola/common/context",
"//java/dev/enola/common/convert",
"//java/dev/enola/common/io",
"//java/dev/enola/common/protobuf",
"//java/dev/enola/common/protobuf:test_java_proto",
"//java/dev/enola/common/yamljson",
"//java/dev/enola/datatype",
"//java/dev/enola/thing:thing_java",
"//java/dev/enola/thing:thing_java_proto",
"//test",
Expand Down
9 changes: 9 additions & 0 deletions java/dev/enola/rdf/RdfResourceIntoThingConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
*/
package dev.enola.rdf;

import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableList;

import dev.enola.common.context.TLC;
import dev.enola.common.convert.ConversionException;
import dev.enola.common.io.resource.ReadableResource;
import dev.enola.datatype.DatatypeRepository;
Expand All @@ -35,6 +37,8 @@
import java.util.Optional;
import java.util.function.Supplier;

@SuppressWarnings("rawtypes")
@AutoService(ResourceIntoThingConverter.class)
public class RdfResourceIntoThingConverter<T extends Thing>
implements ResourceIntoThingConverter<T> {

Expand All @@ -56,9 +60,14 @@ public RdfResourceIntoThingConverter(

@SuppressWarnings("unchecked")
public RdfResourceIntoThingConverter(DatatypeRepository datatypeRepository) {
// TODO Instead ImmutableThing.builder() it should look-up GenJavaThing subclass, if any
this(datatypeRepository, () -> (Builder<T>) ImmutableThing.builder());
}

public RdfResourceIntoThingConverter() {
this(TLC.get(DatatypeRepository.class));
}

@Override
public Optional<List<Builder<T>>> convert(ReadableResource input) throws ConversionException {
var optProtoList = rdfResourceIntoProtoThingConverter.convert(input);
Expand Down
42 changes: 42 additions & 0 deletions java/dev/enola/rdf/RdfResourceIntoThingConverterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.rdf;

import static com.google.common.truth.Truth.assertThat;

import dev.enola.common.context.TLC;
import dev.enola.common.io.resource.ClasspathResource;
import dev.enola.datatype.DatatypeRepository;
import dev.enola.datatype.DatatypeRepositoryBuilder;
import dev.enola.thing.Thing;
import dev.enola.thing.io.ResourceIntoThingConverters;

import org.junit.Test;

public class RdfResourceIntoThingConverterTest {

@Test
public void convert() {
try (var ctx = TLC.open()) {
ctx.push(DatatypeRepository.class, new DatatypeRepositoryBuilder().build());
ResourceIntoThingConverters<Thing> c = new ResourceIntoThingConverters<>();
var thing = c.convert(new ClasspathResource("picasso.ttl")).getFirst().build();
assertThat(thing.iri()).isEqualTo("http://example.enola.dev/Dalí");
}
}
}
3 changes: 3 additions & 0 deletions java/dev/enola/thing/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ java_proto_library(
java_library(
name = "thing_java",
srcs = glob(
# TODO Separate BUILD for each sub-package
["**/*.java"],
exclude = [
"**/*Test*.java",
Expand All @@ -71,6 +72,8 @@ java_library(
deps = [
":thing_java_proto",
"//java/dev/enola/common",
"//java/dev/enola/common/collect",
"//java/dev/enola/common/context",
"//java/dev/enola/common/convert",
"//java/dev/enola/common/function",
"//java/dev/enola/common/io",
Expand Down
Loading

0 comments on commit dc4d0d5

Please sign in to comment.