From 2d204a14b58c840cea2d0281aa2f3e32fea4c23b Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Tue, 8 Oct 2024 21:26:11 +0200 Subject: [PATCH] feat (core): Graph Commons JSON Converter --- docs/use/rosetta/index.md | 9 +++ java/dev/enola/cli/CommandWithIRI.java | 7 ++ java/dev/enola/cli/Configuration.java | 2 + java/dev/enola/cli/Format.java | 4 + java/dev/enola/core/rosetta/Rosetta.java | 2 + java/dev/enola/thing/gen/BUILD | 1 + .../GraphCommonsJsonGenerator.java | 73 +++++++++++++++++++ .../graphcommons/GraphCommonsMediaType.java | 38 ++++++++++ .../GraphCommonsResourceConverter.java | 45 ++++++++++++ .../thing/gen/graphviz/GraphvizGenerator.java | 6 +- java/dev/enola/thing/io/ThingMediaTypes.java | 2 + 11 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 java/dev/enola/thing/gen/graphcommons/GraphCommonsJsonGenerator.java create mode 100644 java/dev/enola/thing/gen/graphcommons/GraphCommonsMediaType.java create mode 100644 java/dev/enola/thing/gen/graphcommons/GraphCommonsResourceConverter.java diff --git a/docs/use/rosetta/index.md b/docs/use/rosetta/index.md index 68d231ed..87314611 100644 --- a/docs/use/rosetta/index.md +++ b/docs/use/rosetta/index.md @@ -63,6 +63,15 @@ $ ./enola rosetta --no-file-loader --in test/picasso.ttl --out "docs/BUILT/picas ![Smaller Graph of Painters](../../BUILT/picasso-small.gv.svg) +### Graph Commons + +```bash cd ../.././.. +$ ./enola rosetta --in enola:TikaMediaTypes --out /tmp/TikaMediaTypes.graphcommons.json +... +``` + +produces a JSON which can be imported into [GraphCommons.com](https://graphcommons.com/). + ### GEXF ```bash cd ../.././.. diff --git a/java/dev/enola/cli/CommandWithIRI.java b/java/dev/enola/cli/CommandWithIRI.java index af6b5f0d..ab0c32e8 100644 --- a/java/dev/enola/cli/CommandWithIRI.java +++ b/java/dev/enola/cli/CommandWithIRI.java @@ -30,6 +30,7 @@ import dev.enola.core.view.EnolaMessages; import dev.enola.rdf.io.RdfWriterConverter; import dev.enola.rdf.proto.ProtoThingRdfConverter; +import dev.enola.thing.gen.graphcommons.GraphCommonsJsonGenerator; import dev.enola.thing.gen.graphviz.GraphvizGenerator; import dev.enola.thing.message.ProtoThings; import dev.enola.thing.metadata.ThingMetadataProvider; @@ -108,6 +109,12 @@ protected void write(Message thing) throws IOException { return; } + if (Format.GraphCommons.equals(format) && thing instanceof Things protoThings) { + var javaThings = ProtoThings.proto2java(protoThings.getThingsList()); + new GraphCommonsJsonGenerator().convertIntoOrThrow(javaThings, resource); + return; + } + // Otherwise new ProtoIO(typeRegistryWrapper.get()).write(thing, resource); } diff --git a/java/dev/enola/cli/Configuration.java b/java/dev/enola/cli/Configuration.java index 52983230..06e53cc3 100644 --- a/java/dev/enola/cli/Configuration.java +++ b/java/dev/enola/cli/Configuration.java @@ -29,6 +29,7 @@ import dev.enola.rdf.io.RdfMediaTypeYamlLd; import dev.enola.rdf.io.RdfMediaTypes; import dev.enola.thing.gen.gexf.GexfMediaType; +import dev.enola.thing.gen.graphcommons.GraphCommonsMediaType; import dev.enola.thing.gen.graphviz.GraphvizMediaType; import dev.enola.thing.io.ThingMediaTypes; @@ -46,6 +47,7 @@ class Configuration { new MarkdownMediaTypes(), new GraphvizMediaType(), new GexfMediaType(), + new GraphCommonsMediaType(), new DatalogMediaTypes(), new StandardMediaTypes(), new YamlMediaType(), diff --git a/java/dev/enola/cli/Format.java b/java/dev/enola/cli/Format.java index f7b523d2..227a2348 100644 --- a/java/dev/enola/cli/Format.java +++ b/java/dev/enola/cli/Format.java @@ -21,6 +21,7 @@ import dev.enola.common.protobuf.ProtobufMediaTypes; import dev.enola.rdf.io.RdfMediaTypes; +import dev.enola.thing.gen.graphcommons.GraphCommonsMediaType; import dev.enola.thing.gen.graphviz.GraphvizMediaType; public enum Format { @@ -30,6 +31,8 @@ public enum Format { Graphviz, + GraphCommons, + TextProto, ProtoYAML, @@ -43,6 +46,7 @@ MediaType toMediaType() { case Turtle -> RdfMediaTypes.TURTLE; case JSONLD -> RdfMediaTypes.JSON_LD; case Graphviz -> GraphvizMediaType.GV; + case GraphCommons -> GraphCommonsMediaType.GCJ; case TextProto -> ProtobufMediaTypes.PROTOBUF_TEXTPROTO_UTF_8; case ProtoYAML -> ProtobufMediaTypes.PROTOBUF_YAML_UTF_8; diff --git a/java/dev/enola/core/rosetta/Rosetta.java b/java/dev/enola/core/rosetta/Rosetta.java index a6534818..af97b76c 100644 --- a/java/dev/enola/core/rosetta/Rosetta.java +++ b/java/dev/enola/core/rosetta/Rosetta.java @@ -38,6 +38,7 @@ import dev.enola.rdf.io.RdfResourceConverter; import dev.enola.thing.gen.gexf.GexfGenerator; import dev.enola.thing.gen.gexf.GexfResourceConverter; +import dev.enola.thing.gen.graphcommons.GraphCommonsResourceConverter; import dev.enola.thing.gen.graphviz.GraphvizGenerator; import dev.enola.thing.gen.graphviz.GraphvizResourceConverter; import dev.enola.thing.io.Loader; @@ -103,6 +104,7 @@ public Rosetta(ResourceProvider rp, Loader loader) { new YamlJsonResourceConverter(), new GraphvizResourceConverter(loader, new GraphvizGenerator(tmp)), new GexfResourceConverter(loader, new GexfGenerator(tmp)), + new GraphCommonsResourceConverter(loader), new XmlResourceConverter(rp), new CharResourceConverter())); // NOT new IdempotentCopyingResourceNonConverter() diff --git a/java/dev/enola/thing/gen/BUILD b/java/dev/enola/thing/gen/BUILD index 93007030..64d1c247 100644 --- a/java/dev/enola/thing/gen/BUILD +++ b/java/dev/enola/thing/gen/BUILD @@ -49,6 +49,7 @@ java_library( "@maven//:com_google_auto_service_auto_service_annotations", "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_errorprone_error_prone_type_annotations", + "@maven//:com_google_code_gson_gson", "@maven//:com_google_guava_guava", "@maven//:org_jspecify_jspecify", "@maven//:org_slf4j_slf4j_api", diff --git a/java/dev/enola/thing/gen/graphcommons/GraphCommonsJsonGenerator.java b/java/dev/enola/thing/gen/graphcommons/GraphCommonsJsonGenerator.java new file mode 100644 index 00000000..ffae6eb9 --- /dev/null +++ b/java/dev/enola/thing/gen/graphcommons/GraphCommonsJsonGenerator.java @@ -0,0 +1,73 @@ +/* + * 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.thing.gen.graphcommons; + +import static com.google.gson.FormattingStyle.PRETTY; +import static com.google.gson.Strictness.STRICT; + +import com.google.common.io.CharStreams; +import com.google.gson.stream.JsonWriter; + +import dev.enola.common.context.TLC; +import dev.enola.common.convert.ConversionException; +import dev.enola.thing.Thing; +import dev.enola.thing.gen.ThingsIntoAppendableConverter; +import dev.enola.thing.repo.StackedThingProvider; +import dev.enola.thing.repo.ThingProvider; + +import java.io.IOException; + +/** Generator of JSON Format used by 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.gen.graphcommons; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.common.net.MediaType; + +import dev.enola.common.io.mediatype.MediaTypeProvider; + +import java.nio.charset.StandardCharsets; + +public class GraphCommonsMediaType implements MediaTypeProvider { + + public static final MediaType GCJ = + MediaType.create("text", "vnd.enola.graphcommons+json") + .withCharset(StandardCharsets.UTF_8); + + @Override + public Multimap extensionsToTypes() { + return ImmutableMultimap.of(".graphcommons.json", GCJ); + } +} diff --git a/java/dev/enola/thing/gen/graphcommons/GraphCommonsResourceConverter.java b/java/dev/enola/thing/gen/graphcommons/GraphCommonsResourceConverter.java new file mode 100644 index 00000000..b0fb4749 --- /dev/null +++ b/java/dev/enola/thing/gen/graphcommons/GraphCommonsResourceConverter.java @@ -0,0 +1,45 @@ +/* + * 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.thing.gen.graphcommons; + +import dev.enola.common.io.mediatype.MediaTypes; +import dev.enola.common.io.resource.ReadableResource; +import dev.enola.common.io.resource.WritableResource; +import dev.enola.common.io.resource.convert.CatchingResourceConverter; +import dev.enola.thing.io.Loader; + +public class GraphCommonsResourceConverter implements CatchingResourceConverter { + + private final GraphCommonsJsonGenerator graphcommons = new GraphCommonsJsonGenerator(); + private final Loader loader; + + public GraphCommonsResourceConverter(Loader loader) { + this.loader = loader; + } + + @Override + public boolean convertIntoThrows(ReadableResource from, WritableResource into) + throws Exception { + if (!MediaTypes.normalizedNoParamsEquals(into.mediaType(), GraphCommonsMediaType.GCJ)) + return false; + + var things = loader.loadAtLeastOneThing(from.uri()); + graphcommons.convertIntoOrThrow(things, into); + return true; + } +} diff --git a/java/dev/enola/thing/gen/graphviz/GraphvizGenerator.java b/java/dev/enola/thing/gen/graphviz/GraphvizGenerator.java index ef788c7c..43266570 100644 --- a/java/dev/enola/thing/gen/graphviz/GraphvizGenerator.java +++ b/java/dev/enola/thing/gen/graphviz/GraphvizGenerator.java @@ -87,21 +87,21 @@ public boolean convertInto(Iterable from, Appendable out) ctx.push(ThingProvider.class, new StackedThingProvider(from)); for (Thing thing : from) { thingIRIs.add(thing.iri()); - printFullThing(thing, out, thingIRIs, linkIRIs); + printThing(thing, out, thingIRIs, linkIRIs); } // Remove links to all things which were processed after we processed them linkIRIs.removeAll(thingIRIs); // linkIRIs now contains things which were linked to but that have no properties for (String orphanIRI : linkIRIs) { var orphanThing = new OnlyIRIThing(orphanIRI); - printFullThing(orphanThing, out, thingIRIs, linkIRIs); + printThing(orphanThing, out, thingIRIs, linkIRIs); } } out.append("}\n"); return true; } - private void printFullThing( + private void printThing( Thing thing, Appendable out, Set thingIRIs, Set linkIRIs) throws IOException { boolean full = TLC.optional(Flags.FULL).orElse(false); diff --git a/java/dev/enola/thing/io/ThingMediaTypes.java b/java/dev/enola/thing/io/ThingMediaTypes.java index 5b676a01..3366685a 100644 --- a/java/dev/enola/thing/io/ThingMediaTypes.java +++ b/java/dev/enola/thing/io/ThingMediaTypes.java @@ -34,6 +34,8 @@ public class ThingMediaTypes implements MediaTypeProvider { + // TODO Use vnd.enola.* like e.g. in GraphCommonsMediaType (for consistency) + public static final MediaType THING_TEXTPROTO_UTF_8 = ProtobufMediaTypes.setProtoMessageFQN( ProtobufMediaTypes.PROTOBUF_TEXTPROTO_UTF_8,