diff --git a/java/dev/enola/common/collect/BUILD b/java/dev/enola/common/collect/BUILD new file mode 100644 index 000000000..85b49d3e1 --- /dev/null +++ b/java/dev/enola/common/collect/BUILD @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright 2023 The Enola 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", + ], +) diff --git a/java/dev/enola/common/collect/Immutables.java b/java/dev/enola/common/collect/Immutables.java new file mode 100644 index 000000000..5313f4ab7 --- /dev/null +++ b/java/dev/enola/common/collect/Immutables.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2024 The Enola 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 com.google.common.collect.ImmutableList; + +import java.util.List; + +public final class Immutables { + + public static ImmutableList join(List a, List b) { + var builder = ImmutableList.builderWithExpectedSize(a.size() + b.size()); + return builder.addAll(a).addAll(b).build(); + } + + private Immutables() {} +} diff --git a/java/dev/enola/thing/BUILD b/java/dev/enola/thing/BUILD index cdbd94647..4f9608087 100644 --- a/java/dev/enola/thing/BUILD +++ b/java/dev/enola/thing/BUILD @@ -71,6 +71,7 @@ java_library( deps = [ ":thing_java_proto", "//java/dev/enola/common", + "//java/dev/enola/common/collect", "//java/dev/enola/common/convert", "//java/dev/enola/common/function", "//java/dev/enola/common/io", diff --git a/java/dev/enola/thing/io/Loader.java b/java/dev/enola/thing/io/Loader.java index 1108a2000..72b2adc26 100644 --- a/java/dev/enola/thing/io/Loader.java +++ b/java/dev/enola/thing/io/Loader.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.util.List; import java.util.stream.Stream; public class Loader implements ConverterInto, Store> { @@ -58,7 +59,7 @@ private void load(ReadableResource resource, Store store) { things.get() .forEach( thingBuilder -> { - thingBuilder.set(KIRI.E.ORIGIN, resource.uri()); + thingBuilder.set(KIRI.E.ORIGIN, List.of(resource.uri())); var thing = thingBuilder.build(); store.merge(thing); }); diff --git a/java/dev/enola/thing/repo/ThingMerger.java b/java/dev/enola/thing/repo/ThingMerger.java index d9dfb3e40..24ef0157d 100644 --- a/java/dev/enola/thing/repo/ThingMerger.java +++ b/java/dev/enola/thing/repo/ThingMerger.java @@ -17,38 +17,42 @@ */ package dev.enola.thing.repo; +import static dev.enola.common.collect.Immutables.join; + import dev.enola.thing.KIRI; import dev.enola.thing.Thing; +import java.util.List; + class ThingMerger { // TODO Implement missing ThingMergerTest coverage! - public static Thing merge(Thing existing, Thing update) { - if (!existing.iri().equals(update.iri())) throw new IllegalArgumentException(); + public static Thing merge(Thing t1, Thing t2) { + if (!t1.iri().equals(t2.iri())) throw new IllegalArgumentException(); - if (existing.predicateIRIs().isEmpty()) return update; - if (update.predicateIRIs().isEmpty()) return existing; + if (t1.predicateIRIs().isEmpty()) return t2; + if (t2.predicateIRIs().isEmpty()) return t1; - var merged = existing.copy(); - var properties = update.properties(); + var merged = t1.copy(); + var properties = t2.properties(); properties.forEach( - (predicate, value) -> { - var old = existing.get(predicate); - if (old == null) merged.set(predicate, value, update.datatype(predicate)); - else if (old.equals(value)) { + (predicate, t2obj) -> { + var t1obj = t1.get(predicate); + if (t1obj == null) merged.set(predicate, t2obj, t2.datatype(predicate)); + else if (t1obj.equals(t2obj)) { // That's fine! - } else if (predicate.equals(KIRI.E.ORIGIN)) { - // TODO Implement merging both into a List, with test coverage! + } else if (t1obj instanceof List t1list && t2obj instanceof List t2list) { + merged.set(predicate, join(t1list, t2list)); } else throw new IllegalStateException( "Cannot merge " + predicate + " of an " - + existing.iri() + + t1.iri() + " from " - + existing.getString(KIRI.E.ORIGIN) + + t1.getString(KIRI.E.ORIGIN) + " and " - + update.getString(KIRI.E.ORIGIN)); + + t2.getString(KIRI.E.ORIGIN)); }); return merged.build(); }