Skip to content

Commit

Permalink
feat (core): Make MutablePredicatesObjects implement Builder2
Browse files Browse the repository at this point in the history
  • Loading branch information
vorburger committed Sep 29, 2024
1 parent aa9c6db commit dba5124
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 51 deletions.
4 changes: 2 additions & 2 deletions java/dev/enola/thing/PredicatesObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ interface Builder2<B extends PredicatesObjects> extends PredicatesObjects.Builde
* Adds one of possibly several value objects for the given predicate IRI.
*
* <p>This is UNORDERED! Insertion order may NOT be preserved. Duplicates are not allowed
* and cause an error. It is an error if this property has already been set to anything else
* than a {@link Set}.
* and will cause an error (possibly only on {@link #build()}). It is an error if this
* property has already been set to anything else than a {@link Set}.
*/
<@ImmutableTypeParameter T> PredicatesObjects.Builder2<B> add(String predicateIRI, T value);

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

import com.google.common.collect.ImmutableCollection;

import dev.enola.thing.PredicatesObjects;
import dev.enola.thing.Thing;

final class ImmutableObjects {

static void check(Object object) {
if ((object instanceof Iterable<?>) && !(object instanceof ImmutableCollection<?>))
throw new IllegalStateException("Non-ImmutableCollection: " + object);
if (object instanceof Thing)
throw new IllegalStateException("Things cannot contain Things: " + object);
if (object instanceof Thing.Builder)
throw new IllegalStateException("Things cannot contain Thing.Builder: " + object);
if (object instanceof PredicatesObjects.Builder)
throw new IllegalStateException(
"Things cannot contain PredicatesObjects.Builder: " + object);
if (object instanceof PredicatesObjects && !(object instanceof IImmutablePredicatesObjects))
throw new IllegalStateException(
"Things cannot contain Non-IImmutablePredicatesObjects: " + object);
}

private ImmutableObjects() {}
}
13 changes: 9 additions & 4 deletions java/dev/enola/thing/impl/ImmutablePredicatesObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import dev.enola.thing.Literal;
import dev.enola.thing.PredicatesObjects;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import org.jspecify.annotations.Nullable;

@Immutable
Expand Down Expand Up @@ -99,11 +101,12 @@ public PredicatesObjects.Builder<? extends ImmutablePredicatesObjects> copy() {
return new Builder<>(properties(), datatypes());
}

private static final class Builder<B extends PredicatesObjects>
@SuppressFBWarnings("NM_SAME_SIMPLE_NAME_AS_INTERFACE")
static class Builder<B extends PredicatesObjects> // skipcq: JAVA-E0169
implements PredicatesObjects.Builder<B> {

private final ImmutableMap.Builder<String, Object> properties;
private final ImmutableMap.Builder<String, String> datatypes;
protected final ImmutableMap.Builder<String, Object> properties;
protected final ImmutableMap.Builder<String, String> datatypes;

Builder() {
properties = ImmutableMap.builder();
Expand All @@ -128,6 +131,7 @@ private static final class Builder<B extends PredicatesObjects>

@Override
public PredicatesObjects.Builder<B> set(String predicateIRI, Object value) {
ImmutableObjects.check(value);
if (value instanceof Literal literal)
set(predicateIRI, literal.value(), literal.datatypeIRI());
else properties.put(predicateIRI, value);
Expand All @@ -136,7 +140,8 @@ public PredicatesObjects.Builder<B> set(String predicateIRI, Object value) {

@Override
public PredicatesObjects.Builder<B> set(
String predicateIRI, Object value, String datatypeIRI) {
String predicateIRI, Object value, @Nullable String datatypeIRI) {
ImmutableObjects.check(value);
properties.put(predicateIRI, value);
if (datatypeIRI != null) datatypes.put(predicateIRI, datatypeIRI);
return this;
Expand Down
27 changes: 6 additions & 21 deletions java/dev/enola/thing/impl/ImmutableThing.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.google.errorprone.annotations.Immutable;
import com.google.errorprone.annotations.ThreadSafe;

import dev.enola.thing.Literal;
import dev.enola.thing.Thing;
import dev.enola.thing.java2.TBF;

Expand Down Expand Up @@ -91,35 +90,24 @@ public Builder<? extends ImmutableThing> copy() {

@SuppressFBWarnings("NM_SAME_SIMPLE_NAME_AS_INTERFACE")
public static class Builder<B extends IImmutableThing> // skipcq: JAVA-E0169
implements Thing.Builder<B> {
extends ImmutablePredicatesObjects.Builder<B> implements Thing.Builder<B> {

// TODO Make this extend ImmutablePredicatesObjects.Builder instead of copy/paste

protected final ImmutableMap.Builder<String, Object> properties;
protected final ImmutableMap.Builder<String, String> datatypes;
protected @Nullable String iri;

protected Builder() {
properties = ImmutableMap.builder();
datatypes = ImmutableMap.builder();
super();
}

protected Builder(int expectedSize) {
properties = ImmutableMap.builderWithExpectedSize(expectedSize); // exact
datatypes = ImmutableMap.builderWithExpectedSize(expectedSize); // upper bound
super(expectedSize);
}

protected Builder(
String iri,
final ImmutableMap<String, Object> properties,
final ImmutableMap<String, String> datatypes) {
super(properties, datatypes);
iri(iri);
this.properties =
ImmutableMap.<String, Object>builderWithExpectedSize(properties.size())
.putAll(properties);
this.datatypes =
ImmutableMap.<String, String>builderWithExpectedSize(properties.size())
.putAll(datatypes);
}

@Override
Expand All @@ -133,17 +121,14 @@ public Thing.Builder<B> iri(String iri) {

@Override
public Thing.Builder<B> set(String predicateIRI, Object value) {
if (value instanceof Literal literal)
set(predicateIRI, literal.value(), literal.datatypeIRI());
else properties.put(predicateIRI, value);
super.set(predicateIRI, value);
return this;
}

@Override
public Thing.Builder<B> set(
String predicateIRI, Object value, @Nullable String datatypeIRI) {
properties.put(predicateIRI, value);
if (datatypeIRI != null) datatypes.put(predicateIRI, datatypeIRI);
super.set(predicateIRI, value, datatypeIRI);
return this;
}

Expand Down
74 changes: 65 additions & 9 deletions java/dev/enola/thing/impl/MutablePredicatesObjects.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import com.google.common.collect.ImmutableSet;

import dev.enola.thing.PredicatesObjects;
import dev.enola.thing.Thing;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

Expand All @@ -35,11 +34,10 @@
@SuppressFBWarnings("EQ_DOESNT_OVERRIDE_EQUALS")
// skipcq: JAVA-W0100
public class MutablePredicatesObjects<B extends IImmutablePredicatesObjects>
implements PredicatesObjects, PredicatesObjects.Builder<B> {
implements PredicatesObjects, PredicatesObjects.Builder2<B> {

protected @Nullable String iri;
protected final Map<String, Object> properties;
protected final Map<String, String> datatypes;
private final Map<String, Object> properties;
private final Map<String, String> datatypes;

public MutablePredicatesObjects() {
properties = new HashMap<>();
Expand All @@ -52,18 +50,75 @@ public MutablePredicatesObjects(int expectedSize) {
}

@Override
public Builder<B> set(String predicateIRI, Object value) {
public Builder2<B> set(String predicateIRI, Object value) {
properties.put(predicateIRI, value);
return this;
}

@Override
public Builder<B> set(String predicateIRI, Object value, @Nullable String datatypeIRI) {
public Builder2<B> set(String predicateIRI, Object value, @Nullable String datatypeIRI) {
properties.put(predicateIRI, value);
if (datatypeIRI != null) datatypes.put(predicateIRI, datatypeIRI);
return this;
}

@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> Builder2<B> add(String predicateIRI, T value) {
var object = properties.get(predicateIRI);
if (object == null) {
var builder = ImmutableSet.builder();
properties.put(predicateIRI, builder);
builder.add(value);
} else if (object instanceof ImmutableSet.Builder builder) {
builder.add(value);
} else
throw new IllegalStateException(
predicateIRI + " is not an ImmutableSet.Builder: " + object);
return this;
}

@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> Builder2<B> addOrdered(String predicateIRI, T value) {
var object = properties.get(predicateIRI);
if (object == null) {
var builder = ImmutableList.builder();
properties.put(predicateIRI, builder);
builder.add(value);
} else if (object instanceof ImmutableList.Builder builder) {
builder.add(value);
} else
throw new IllegalStateException(
predicateIRI + " is not an ImmutableList.Builder: " + object);
return this;
}

@Override
public <T> Builder2<B> add(String predicateIRI, T value, @Nullable String datatypeIRI) {
checkCollectionDatatype(predicateIRI, datatypeIRI);
add(predicateIRI, value);
return this;
}

@Override
public <T> Builder2<B> addOrdered(String predicateIRI, T value, @Nullable String datatypeIRI) {
checkCollectionDatatype(predicateIRI, datatypeIRI);
addOrdered(predicateIRI, value);
return this;
}

private void checkCollectionDatatype(String predicateIRI, @Nullable String datatypeIRI) {
// Nota bene: This is, of course, actually stricter than what RDF would technically allow...
// ... but this is intentional and matches intended strongly type safe generated code.
if (datatypeIRI != null) {
var previous = datatypes.putIfAbsent(predicateIRI, datatypeIRI);
if (!datatypeIRI.equals(previous))
throw new IllegalStateException(
predicateIRI + " has another Datatype: " + previous);
}
}

@Override
@Deprecated
@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -93,7 +148,7 @@ public Map<String, String> datatypes() {

@Override
@Deprecated
public Builder<? extends PredicatesObjects> copy() {
public Builder2<? extends PredicatesObjects> copy() {
return this;
}

Expand Down Expand Up @@ -130,9 +185,10 @@ protected void deepBuildInto(
var object = entry.getValue();
if (object instanceof List<?> list) object = ImmutableList.copyOf(list);
if (object instanceof Set<?> list) object = ImmutableSet.copyOf(list);
if (object instanceof Thing) throw new IllegalStateException(object.toString());
if (object instanceof MutablePredicatesObjects<?> mutablePredicatesObjects)
object = mutablePredicatesObjects.build();
// Keep these ^^^ conversions in sync with:
ImmutableObjects.check(object);
immutableBuilder.set(predicateIRI, object, datatype(predicateIRI));
}
}
Expand Down
39 changes: 32 additions & 7 deletions java/dev/enola/thing/impl/MutableThing.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
@SuppressFBWarnings("EQ_DOESNT_OVERRIDE_EQUALS")
// skipcq: JAVA-W0100
public class MutableThing<B extends IImmutableThing> extends MutablePredicatesObjects<B>
implements Thing, Thing.Builder<B> {
implements Thing, Thing.Builder2<B> {

protected @Nullable String iri;
private @Nullable String iri;

public MutableThing() {
super();
Expand All @@ -49,7 +49,7 @@ public MutableThing(int expectedSize) {
}

@Override
public Thing.Builder<B> iri(String iri) {
public Thing.Builder2<B> iri(String iri) {
this.iri = iri;
return this;
}
Expand All @@ -61,20 +61,45 @@ public String iri() {
}

@Override
public Thing.Builder<B> set(String predicateIRI, Object value) {
public Thing.Builder2<B> set(String predicateIRI, Object value) {
super.set(predicateIRI, value);
return this;
}

@Override
public Thing.Builder<B> set(String predicateIRI, Object value, @Nullable String datatypeIRI) {
public Thing.Builder2<B> set(String predicateIRI, Object value, @Nullable String datatypeIRI) {
super.set(predicateIRI, value, datatypeIRI);
return this;
}

@Override
public <T> Thing.Builder2<B> add(String predicateIRI, T value) {
super.add(predicateIRI, value);
return this;
}

@Override
public <T> Thing.Builder2<B> add(String predicateIRI, T value, @Nullable String datatypeIRI) {
super.add(predicateIRI, value, datatypeIRI);
return this;
}

@Override
public <T> Thing.Builder2<B> addOrdered(String predicateIRI, T value) {
super.addOrdered(predicateIRI, value);
return this;
}

@Override
public <T> Thing.Builder2<B> addOrdered(
String predicateIRI, T value, @Nullable String datatypeIRI) {
super.addOrdered(predicateIRI, value, datatypeIRI);
return this;
}

@Override
@Deprecated
public Thing.Builder<? extends Thing> copy() {
public Thing.Builder2<? extends Thing> copy() {
return this;
}

Expand All @@ -97,7 +122,7 @@ public final String toString() {
@Override
@SuppressWarnings("unchecked") // TODO How to remove (B) type cast?!
public B build() {
var immutableBuilder = ImmutableThing.builderWithExpectedSize(properties.size());
var immutableBuilder = ImmutableThing.builderWithExpectedSize(predicateIRIs().size());
immutableBuilder.iri(iri);
deepBuildInto(immutableBuilder);
return (B) immutableBuilder.build();
Expand Down
Loading

0 comments on commit dba5124

Please sign in to comment.