Skip to content

Commit

Permalink
feat (core): Graph Commons JSON Converter
Browse files Browse the repository at this point in the history
  • Loading branch information
vorburger committed Dec 12, 2024
1 parent 8e8d83e commit 3e79888
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 3 deletions.
9 changes: 9 additions & 0 deletions docs/use/rosetta/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ../.././..
Expand Down
7 changes: 7 additions & 0 deletions java/dev/enola/cli/CommandWithIRI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions java/dev/enola/cli/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -46,6 +47,7 @@ class Configuration {
new MarkdownMediaTypes(),
new GraphvizMediaType(),
new GexfMediaType(),
new GraphCommonsMediaType(),
new DatalogMediaTypes(),
new StandardMediaTypes(),
new YamlMediaType(),
Expand Down
4 changes: 4 additions & 0 deletions java/dev/enola/cli/Format.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -30,6 +31,8 @@ public enum Format {

Graphviz,

GraphCommons,

TextProto,

ProtoYAML,
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions java/dev/enola/core/rosetta/Rosetta.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down
1 change: 1 addition & 0 deletions java/dev/enola/thing/gen/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.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 <a href="https://graphcommons.com/>Graph Commons</a>. */
public class GraphCommonsJsonGenerator implements ThingsIntoAppendableConverter {

@Override
public boolean convertInto(Iterable<Thing> from, Appendable out)
throws ConversionException, IOException {
var writer = CharStreams.asWriter(out);
var jsonWriter = new JsonWriter(writer);
jsonWriter.setStrictness(STRICT);
jsonWriter.setFormattingStyle(PRETTY); // TODO FormattingStyle.COMPACT, if !pretty
jsonWriter.setSerializeNulls(false);
jsonWriter.setHtmlSafe(true); // TODO ?
jsonWriter.beginObject();
try (var ctx = TLC.open()) {
ctx.push(ThingProvider.class, new StackedThingProvider(from));
jsonWriter.name("nodes").beginArray();
for (Thing thing : from) printThingNode(thing, jsonWriter);
jsonWriter.endArray().name("edges").beginArray();
for (Thing thing : from) printThingEdges(thing, jsonWriter);
jsonWriter.endArray().name("nodeTypes").beginArray();
// TODO printThingNodeTypes()
jsonWriter.endArray().name("edgeTypes").beginArray();
// TODO printThingEdgeTypes()
jsonWriter.endArray().name("name").value("Enola.dev");
}
jsonWriter.endObject();
jsonWriter.flush();
writer.close();
return true;
}

private void printThingNode(Thing thing, JsonWriter jsonWriter) throws IOException {
jsonWriter.beginObject();
jsonWriter.name("id").value(thing.iri());
jsonWriter.endObject();
}

private void printThingEdges(Thing thing, JsonWriter jsonWriter) {}
}
38 changes: 38 additions & 0 deletions java/dev/enola/thing/gen/graphcommons/GraphCommonsMediaType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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.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<String, MediaType> extensionsToTypes() {
return ImmutableMultimap.of(".graphcommons.json", GCJ);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.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;
}
}
6 changes: 3 additions & 3 deletions java/dev/enola/thing/gen/graphviz/GraphvizGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,21 @@ public boolean convertInto(Iterable<Thing> 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<String> thingIRIs, Set<String> linkIRIs)
throws IOException {
boolean full = TLC.optional(Flags.FULL).orElse(false);
Expand Down
2 changes: 2 additions & 0 deletions java/dev/enola/thing/io/ThingMediaTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 3e79888

Please sign in to comment.