001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2024-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.thing.impl;
019
020import com.google.errorprone.annotations.ImmutableTypeParameter;
021
022import dev.enola.thing.HasPredicateIRI;
023import dev.enola.thing.Thing;
024import dev.enola.thing.ThingOrBuilder;
025import dev.enola.thing.java.HasType;
026import dev.enola.thing.java.RdfAnnotations;
027import dev.enola.thing.java.TBF;
028
029import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
030
031import org.jspecify.annotations.Nullable;
032
033/**
034 * Implementation of both {@link Thing} and its {@link Thing.Builder} which is simple and mutable.
035 *
036 * <p>When {@link #build()} then it returns an {@link IImmutableThing} copy of this (NOT itself).
037 *
038 * <p>This implementation is pretty inefficient, for both its runtime performance and memory
039 * consumption, and should only be used "shortly lived"; prefer (building this into a) {@link
040 * IImmutableThing} implementations, such as (typically) the {@link ImmutableThing} or its generated
041 * subclasses, for any objects that will be "kept around".
042 *
043 * <p>This implementation is not thread safe, obviously.
044 */
045// TODO Make MutableThing package private, and let users create them only via the #FACTORY TBF
046@SuppressFBWarnings("EQ_DOESNT_OVERRIDE_EQUALS")
047// skipcq: JAVA-W0100
048public class MutableThing<B extends IImmutableThing> extends MutablePredicatesObjects<B>
049        implements ThingOrBuilder<B> {
050
051    public static final TBF FACTORY =
052            new TBF() {
053                @Override
054                public boolean handles(String typeIRI) {
055                    return true;
056                }
057
058                @Override
059                @SuppressWarnings("Immutable") // TODO Error Prone bug?!
060                public Thing.Builder<Thing> create(String typeIRI) {
061                    return new MutableThing().add(HasType.IRI, typeIRI);
062                }
063
064                @Override
065                @SuppressWarnings({"unchecked", "rawtypes"})
066                public <T extends Thing, TB extends Thing.Builder<T>> TB create(
067                        Class<TB> builderInterface, Class<T> thingInterface) {
068                    var builder = (TB) new MutableThing();
069                    RdfAnnotations.addType(thingInterface, builder);
070                    return builder;
071                }
072
073                @Override
074                @SuppressWarnings({"unchecked", "rawtypes"})
075                public <T extends Thing, TB extends Thing.Builder<T>> TB create(
076                        Class<TB> builderInterface, Class<T> thingInterface, int expectedSize) {
077                    var builder = (TB) new MutableThing(expectedSize);
078                    RdfAnnotations.addType(thingInterface, builder);
079                    return builder;
080                }
081            };
082
083    private @Nullable String iri;
084
085    public MutableThing() {
086        super();
087    }
088
089    public MutableThing(int expectedSize) {
090        super(expectedSize);
091    }
092
093    @Override
094    public Thing.Builder<B> iri(String iri) {
095        this.iri = iri;
096        return this;
097    }
098
099    @Override
100    public String iri() {
101        if (iri == null) throw new IllegalStateException();
102        return iri;
103    }
104
105    @Override
106    public <@ImmutableTypeParameter T> Thing.Builder<B> set(String predicateIRI, T value) {
107        super.set(predicateIRI, value);
108        return this;
109    }
110
111    @Override
112    public <@ImmutableTypeParameter T> Thing.Builder<B> set(
113            String predicateIRI, T value, @Nullable String datatypeIRI) {
114        super.set(predicateIRI, value, datatypeIRI);
115        return this;
116    }
117
118    @Override
119    public <@ImmutableTypeParameter T> Thing.Builder<B> add(String predicateIRI, T value) {
120        super.add(predicateIRI, value);
121        return this;
122    }
123
124    @Override
125    public <@ImmutableTypeParameter T> Thing.Builder<B> addAll(
126            String predicateIRI, Iterable<T> values) {
127        super.addAll(predicateIRI, values);
128        return this;
129    }
130
131    @Override
132    public <@ImmutableTypeParameter T> Thing.Builder<B> add(
133            String predicateIRI, T value, @Nullable String datatypeIRI) {
134        super.add(predicateIRI, value, datatypeIRI);
135        return this;
136    }
137
138    @Override
139    public <@ImmutableTypeParameter T> Thing.Builder<B> addAll(
140            String predicateIRI, Iterable<T> values, @Nullable String datatypeIRI) {
141        super.addAll(predicateIRI, values, datatypeIRI);
142        return this;
143    }
144
145    @Override
146    public <@ImmutableTypeParameter T> Thing.Builder<B> addOrdered(String predicateIRI, T value) {
147        super.addOrdered(predicateIRI, value);
148        return this;
149    }
150
151    @Override
152    public <@ImmutableTypeParameter T> Thing.Builder<B> addOrdered(
153            String predicateIRI, T value, @Nullable String datatypeIRI) {
154        super.addOrdered(predicateIRI, value, datatypeIRI);
155        return this;
156    }
157
158    @Override
159    public <@ImmutableTypeParameter T> Thing.Builder<B> add(HasPredicateIRI predicate, T value) {
160        super.add(predicate, value);
161        return this;
162    }
163
164    @Override
165    public <@ImmutableTypeParameter T> Thing.Builder<B> add(
166            HasPredicateIRI predicate, T value, @Nullable String datatypeIRI) {
167        super.add(predicate, value, datatypeIRI);
168        return this;
169    }
170
171    @Override
172    public <@ImmutableTypeParameter T> Thing.Builder<B> addAll(
173            HasPredicateIRI predicate, Iterable<T> value) {
174        super.addAll(predicate, value);
175        return this;
176    }
177
178    @Override
179    public <@ImmutableTypeParameter T> Thing.Builder<B> addAll(
180            HasPredicateIRI predicate, Iterable<T> value, @Nullable String datatypeIRI) {
181        super.addAll(predicate, value, datatypeIRI);
182        return this;
183    }
184
185    @Override
186    public <@ImmutableTypeParameter T> Thing.Builder<B> addOrdered(
187            HasPredicateIRI predicate, T value) {
188        super.addOrdered(predicate, value);
189        return this;
190    }
191
192    @Override
193    public <@ImmutableTypeParameter T> Thing.Builder<B> addOrdered(
194            HasPredicateIRI predicate, T value, @Nullable String datatypeIRI) {
195        super.addOrdered(predicate, value, datatypeIRI);
196        return this;
197    }
198
199    @Override
200    public Thing.Builder<? extends Thing> copy() {
201        return this;
202    }
203
204    @Override
205    public final int hashCode() {
206        return ThingHashCodeEqualsToString.hashCode(this);
207    }
208
209    @Override
210    @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass", "EqualsDoesntCheckParameterClass"})
211    public final boolean equals(Object obj) {
212        return ThingHashCodeEqualsToString.equals(this, obj);
213    }
214
215    @Override
216    public final String toString() {
217        return ThingHashCodeEqualsToString.toString(this, properties, datatypes);
218    }
219
220    @Override
221    @SuppressWarnings("unchecked")
222    public B build() {
223        if (iri == null) throw new IllegalStateException(PackageLocalConstants.NEEDS_IRI_MESSAGE);
224
225        var pair = ImmutableObjects.build(properties, datatypes);
226        return (B) new ImmutableThing(iri, pair.properties(), pair.datatypes());
227    }
228}