Skip to content

Commit

Permalink
fix (core): TikaMediaTypesThingConverter now sets parent
Browse files Browse the repository at this point in the history
  • Loading branch information
vorburger committed Oct 6, 2024
1 parent 1ee32c0 commit 058bd82
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 22 deletions.
39 changes: 39 additions & 0 deletions java/dev/enola/model/enola/HasChildren.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.model.enola;

import dev.enola.thing.Link;
import dev.enola.thing.Thing;

import org.jspecify.annotations.Nullable;

import java.util.Set;

public interface HasChildren extends Thing {

default @Nullable Set<Link> childrenIRI() {
return get("https://enola.dev/children", Set.class);
}

interface Builder<B extends HasChildren> extends Thing.Builder<B> { // skipcq: JAVA-E0169
default HasChildren.Builder<B> childrenIRI(Set<Link> childrenIRI) {
set("https://enola.dev/children", childrenIRI);
return this;
}
}
}
37 changes: 37 additions & 0 deletions java/dev/enola/model/enola/HasParent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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.model.enola;

import dev.enola.thing.Link;
import dev.enola.thing.Thing;

import org.jspecify.annotations.Nullable;

public interface HasParent extends Thing {

default @Nullable String parentIRI() {
return getString("https://enola.dev/parent");
}

interface Builder<B extends HasParent> extends Thing.Builder<B> { // skipcq: JAVA-E0169
default HasParent.Builder<B> parentIRI(String iri) {
set("https://enola.dev/parent", new Link(iri));
return this;
}
}
}
5 changes: 5 additions & 0 deletions java/dev/enola/model/enola/mediatype/HasFileExtensions.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,10 @@ default HasFileExtensions.Builder<B> addFileExtension(String fileExtension) {
add("https://enola.dev/fileExtensions", fileExtension);
return this;
}

default HasFileExtensions.Builder<B> addAllFileExtensions(Iterable<String> fileExtensions) {
addAll("https://enola.dev/fileExtensions", fileExtensions);
return this;
}
}
}
14 changes: 12 additions & 2 deletions java/dev/enola/model/enola/mediatype/MediaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@
*/
package dev.enola.model.enola.mediatype;

import dev.enola.model.enola.HasChildren;
import dev.enola.model.enola.HasParent;
import dev.enola.model.w3.rdfs.HasComment;
import dev.enola.model.w3.rdfs.HasLabel;
import dev.enola.model.w3.rdfs.HasSeeAlso;

public interface MediaType
extends HasLabel, HasComment, HasSeeAlso, HasFileExtensions, HasMediaType {
extends HasLabel,
HasComment,
HasSeeAlso,
HasFileExtensions,
HasMediaType,
HasParent,
HasChildren {

// In theory: interface Builder<B extends MediaType> extends ...
// In practice, we know we're not going to further extend MediaType, so just:
Expand All @@ -31,5 +39,7 @@ interface Builder // skipcq: JAVA-E0169
HasComment.Builder<MediaType>,
HasSeeAlso.Builder<MediaType>,
HasFileExtensions.Builder<MediaType>,
HasMediaType.Builder<MediaType> {}
HasMediaType.Builder<MediaType>,
HasParent.Builder<MediaType>,
HasChildren.Builder<MediaType> {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,30 +61,48 @@ public boolean convertInto(URI from, TypedThingsBuilder<MediaType, MediaType.Bui
var mediaTypeName = tikaMediaType.toString();
try {
var tikaMimeType = tikaMimeTypes.getRegisteredMimeType(mediaTypeName);
var iri = MediaTypes.toIRI(TikaMediaTypes.toGuava(tikaMediaType));
var iri = toIRI(tikaMediaType);
MediaType.Builder thing =
into.getBuilder(iri, MediaType.Builder.class, MediaType.class);

thing.mediaType(tikaMimeType.getName());
thing.label(tikaMimeType.getAcronym());
thing.comment(tikaMimeType.getDescription());
// TODO thing.addAllFileExtensions(tikaMimeType.getExtensions());
thing.addFileExtension(tikaMimeType.getExtension());
// TODO thing.addAllSeeAlso(tikaMimeType.getLinks())
thing.addAllFileExtensions(tikaMimeType.getExtensions());
thing.addAllSeeAlso(
tikaMimeType.getLinks().stream().map(uri -> uri.toString()).toList());

// TODO var uniformTypeIdentifier = tikaMimeType.getUniformTypeIdentifier();
// TODO var hasMagic = tikaMimeType.hasMagic();

tikaMediaTypeRegistry.getChildTypes(tikaMediaType);
tikaMediaTypeRegistry.getSupertype(tikaMediaType);
// TODO Tika hard-codes :( a few special cases, and doesn't e.g. do +json...
var superType = tikaMediaTypeRegistry.getSupertype(tikaMediaType);
if (superType != null) thing.parentIRI(toIRI(superType));

var baseType = tikaMediaType.getBaseType();
if (!baseType.equals(tikaMediaType)) {}
// tikaMediaType.getBaseType() is a superset of getSupertype()

// TODO Making the following a 1 liner...
// TODO Remove this once children are automagically set by generic Inference!!
// TODO Uncomment, once GraphvizGenerator more nicely coalesces parent & children
/*
var children = tikaMediaTypeRegistry.getChildTypes(tikaMediaType);
if (!children.isEmpty()) {
var childrenIRI = ImmutableSet.<Link>builderWithExpectedSize(children.size());
for (var child : children) {
childrenIRI.add(new Link(toIRI(child)));
}
thing.childrenIRI(childrenIRI.build());
}
*/

} catch (MimeTypeException e) {
LOG.warn("MediaType not found: {}", mediaTypeName, e);
}
}
return true;
}

private String toIRI(org.apache.tika.mime.MediaType tikaMediaType) {
return MediaTypes.toIRI(TikaMediaTypes.toGuava(tikaMediaType));
}
}
6 changes: 6 additions & 0 deletions java/dev/enola/model/w3/rdfs/HasSeeAlso.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package dev.enola.model.w3.rdfs;

import dev.enola.thing.KIRI;
import dev.enola.thing.Thing;

import org.jspecify.annotations.Nullable;
Expand All @@ -34,5 +35,10 @@ default HasSeeAlso.Builder<B> addSeeAlso(String seeAlso) {
add(IRI.Predicate.seeAlso, seeAlso);
return this;
}

default HasSeeAlso.Builder<B> addAllSeeAlso(Iterable<String> seeAlso) {
addAll(IRI.Predicate.seeAlso, seeAlso, KIRI.SCHEMA.URL_DATATYPE);
return this;
}
}
}
11 changes: 7 additions & 4 deletions java/dev/enola/thing/KIRI.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,16 @@ public static final class SCHEMA {
public static final String LOGO = NS + "logo";

/**
* URL 🔗 of the Thing, see https://schema.org/url. You *CAN* always http GET an URL. This
* is NOT the same as a logical URI/IRI, and thus not be to confused with the {@link #ID}.
* One example of this could be e.g. its use in Thing "metadata" about a file: URL; this
* would point to the actual file itself.
* IRI of Property for URL 🔗 of the Thing, see https://schema.org/url. You *CAN* always
* http GET an URL. This is NOT the same as a logical URI/IRI, and thus not be to confused
* with the {@link #ID}. One example of this could be e.g. its use in Thing "metadata" about
* a file: URL; this would point to the actual file itself.
*/
public static final String URL = NS + "url";

/** IRI of URL Datatype. Used to mark properties which are links to webpages. */
public static final String URL_DATATYPE = NS + "URL";

/**
* IRI of a Thing which is "the 🪞 same as this one", see https://schema.org/sameAs. For
* example, the URL of a Wikipedia article about it.
Expand Down
1 change: 1 addition & 0 deletions java/dev/enola/thing/Link.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* be returned by {@link Thing#get(String)} and distinguished from a String which is not an IRI but
* text.
*/
// TODO Consider using a Datatype to indicate link? But which...
// TODO Abandon this and just use java.net.URI in Things instead?!
// Or change this record to a class and have an URI field, for 1 time conversion.
// TODO Make it extend Thing; and voilà, it's a Property Graph!
Expand Down
6 changes: 6 additions & 0 deletions java/dev/enola/thing/PredicatesObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,15 @@ interface Builder2<B extends PredicatesObjects> extends PredicatesObjects.Builde
*/
<@ImmutableTypeParameter T> PredicatesObjects.Builder2<B> add(String predicateIRI, T value);

<@ImmutableTypeParameter T> PredicatesObjects.Builder2<B> addAll(
String predicateIRI, Iterable<T> value);

<@ImmutableTypeParameter T> PredicatesObjects.Builder2<B> add(
String predicateIRI, T value, @Nullable String datatypeIRI);

<@ImmutableTypeParameter T> PredicatesObjects.Builder2<B> addAll(
String predicateIRI, Iterable<T> value, @Nullable String datatypeIRI);

/**
* Adds one of possibly several value objects for the given predicate IRI - and preserves
* order.
Expand Down
14 changes: 14 additions & 0 deletions java/dev/enola/thing/Thing.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,31 @@ interface Builder2<B extends Thing> // skipcq: JAVA-E0169

<T> Thing.Builder2<B> add(String predicateIRI, T value);

<T> Thing.Builder2<B> addAll(String predicateIRI, Iterable<T> value);

default <T> Thing.Builder2<B> add(HasPredicateIRI predicate, T value) {
return add(predicate.iri(), value);
}

default <T> Thing.Builder2<B> addAll(HasPredicateIRI predicate, Iterable<T> value) {
return addAll(predicate.iri(), value);
}

<T> Thing.Builder2<B> add(String predicateIRI, T value, @Nullable String datatypeIRI);

<T> Thing.Builder2<B> addAll(
String predicateIRI, Iterable<T> value, @Nullable String datatypeIRI);

default <T> Thing.Builder2<B> add(
HasPredicateIRI predicate, T value, @Nullable String datatypeIRI) {
return add(predicate.iri(), value, datatypeIRI);
}

default <T> Thing.Builder2<B> addAll(
HasPredicateIRI predicate, Iterable<T> value, @Nullable String datatypeIRI) {
return addAll(predicate.iri(), value, datatypeIRI);
}

<T> Thing.Builder2<B> addOrdered(String predicateIRI, T value);

default <T> Thing.Builder2<B> addOrdered(HasPredicateIRI predicate, T value) {
Expand Down
10 changes: 10 additions & 0 deletions java/dev/enola/thing/ThingTester.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.junit.Before;
import org.junit.Test;

import java.util.Set;

public abstract class ThingTester {

// TODO Move some of the generic tests from ImmutableThingTest up here
Expand Down Expand Up @@ -108,4 +110,12 @@ public void setEmptyStringIsIgnored() {
var thing = thingBuilder.build();
assertThat(thing.predicateIRIs()).isEmpty();
}

@Test
public void setEmptyCollectionIsIgnored() {
thingBuilder.iri(THING_IRI);
thingBuilder.set(PREDICATE_IRI, Set.of());
var thing = thingBuilder.build();
assertThat(thing.predicateIRIs()).isEmpty();
}
}
13 changes: 9 additions & 4 deletions java/dev/enola/thing/gen/graphviz/GraphvizGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@

public class GraphvizGenerator implements ThingsIntoAppendableConverter {

// TODO Coalesce e.g. enola:parent & enola:children (schema:inverseOf) into single dir=both link
// This would be useful e.g. for the TikaMediaTypesThingConverter produced graph diagram
// Note that strict digraph graphName { concentrate=true does not do this (because of labels;
// see https://stackoverflow.com/a/3463332/421602).

// TODO Subgraphs? https://graphviz.org/doc/info/lang.html#subgraphs-and-clusters Classes?

// TODO Links to other Things (not external HTTP) from within nested blank nodes? With ports??

private static final int MAX_TEXT_LENGTH = 23;

// NB: RosettaTest#testGraphviz() is the test coverage for this code
Expand All @@ -51,10 +60,6 @@ public class GraphvizGenerator implements ThingsIntoAppendableConverter {

// NB: We're intentionally *NOT* showing the Datatype of properties (it's "too much")-

// TODO Subgraphs? https://graphviz.org/doc/info/lang.html#subgraphs-and-clusters Classes?

// TODO Links to other Things (not external HTTP) from within nested blank nodes? With ports??

private final ThingMetadataProvider metadataProvider;

public GraphvizGenerator(ThingMetadataProvider metadataProvider) {
Expand Down
3 changes: 3 additions & 0 deletions java/dev/enola/thing/impl/ImmutablePredicatesObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.ThreadSafe;

Expand Down Expand Up @@ -137,6 +138,7 @@ static class Builder<B extends PredicatesObjects> // skipcq: JAVA-E0169
public PredicatesObjects.Builder<B> set(String predicateIRI, Object value) {
if (value == null) return this;
if (value instanceof String string && string.isEmpty()) return this;
if (value instanceof Iterable iterable && Iterables.isEmpty(iterable)) return this;
ImmutableObjects.check(value);
if (value instanceof Literal literal)
set(predicateIRI, literal.value(), literal.datatypeIRI());
Expand All @@ -149,6 +151,7 @@ public PredicatesObjects.Builder<B> set(
String predicateIRI, Object value, @Nullable String datatypeIRI) {
if (value == null) return this;
if (value instanceof String string && string.isEmpty()) return this;
if (value instanceof Iterable iterable && Iterables.isEmpty(iterable)) return this;
ImmutableObjects.check(value);
if (datatypeIRI != null) {
if (value instanceof Literal)
Expand Down
Loading

0 comments on commit 058bd82

Please sign in to comment.