From ba09e99adab70c8a25627801ff14eed8962f95df Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Mon, 1 Jun 2026 18:43:11 +0100 Subject: [PATCH 1/2] Auto-generate the classes from the refinhslot package. Now that the code generator in LinkML-Py (at least in my dev branch) can generate suitable code for "refined inherited slots", we no longer need manually crafted test classes, we can instead generate them from a LinkML schema like the rest of the test classes. --- .../test/linkml/refined-inherited-slots.yaml | 60 +++++++++++++++++++ pycodegen/javagen.py | 2 +- 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 core/src/test/linkml/refined-inherited-slots.yaml diff --git a/core/src/test/linkml/refined-inherited-slots.yaml b/core/src/test/linkml/refined-inherited-slots.yaml new file mode 100644 index 0000000..7a28aa1 --- /dev/null +++ b/core/src/test/linkml/refined-inherited-slots.yaml @@ -0,0 +1,60 @@ +id: https://w3id.org/linkml/tests/refined_derived_slots +name: refined_derived_slots +title: Test case for refined derived slots +description: |- + This schema is intended to illustrate and test the case where a + derived class refines (through `slot_usage`) an inherited slot in such + a way that the inherited-and-refined slot has a more specialised range + than the original slot. + +prefixes: + linkml: https://w3id.org/linkml/ +imports: + - linkml:types +default_range: string + +slots: + bar: + range: Bar + bars: + range: Bar + multivalued: true + +classes: + Foo: + attributes: + bar: + range: Bar + bars: + range: Bar + multivalued: true + FirstDerivedFoo: + is_a: Foo + slot_usage: + bar: + range: FirstDerivedBar + bars: + range: FirstDerivedBar + SecondDerivedFoo: + is_a: FirstDerivedFoo + ThirdDerivedFoo: + is_a: SecondDerivedFoo + slot_usage: + bar: + range: SecondDerivedBar + bars: + range: SecondDerivedBar + + Bar: + attributes: + name: + FirstDerivedBar: + is_a: Bar + attributes: + length: + range: integer + SecondDerivedBar: + is_a: FirstDerivedBar + attributes: + width: + range: integer diff --git a/pycodegen/javagen.py b/pycodegen/javagen.py index 82e49c8..06c006d 100644 --- a/pycodegen/javagen.py +++ b/pycodegen/javagen.py @@ -19,7 +19,7 @@ def cleanup_dir(directory): def cli(): # Generating test code - for name, package in [("samples", "base")]: + for name, package in [("samples", "base"), ("refined-inherited-slots", "refinhslot")]: output_dir = ROOT / "core/src/test/java/org/incenp/linkml/core/samples" / package cleanup_dir(output_dir) gen = JavaGenerator(ROOT / f"core/src/test/linkml/{name}.yaml", From 6652cbd42137820b3d09924e48fb720c857864a3 Mon Sep 17 00:00:00 2001 From: Damien Goutte-Gattat Date: Tue, 2 Jun 2026 08:57:11 +0100 Subject: [PATCH 2/2] Re-generate all LinkML-derived code. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use my development branch (corresponding to LinkML PR https://github.com/linkml/linkml/pull/3589) to re-generate all LinkML-derived classes. This replaces the previously manually written classes for the "refined inherited slots" test case by classes that are derived from the corresponding schema. This also updates some classes of LinkML’s own meta-schema, since a handful of them do fall in the "refined inherited slots" scenario (e.g. SlotDefinition refines the `is_a` slot so that it can only point to another SlotDefinition, rather than a more generic Definition). --- .../linkml/core/samples/refinhslot/Bar.java | 75 +++++++-- .../samples/refinhslot/FirstDerivedBar.java | 85 ++++++++-- .../samples/refinhslot/FirstDerivedFoo.java | 140 ++++++++-------- .../linkml/core/samples/refinhslot/Foo.java | 151 +++++++++-------- .../samples/refinhslot/SecondDerivedBar.java | 89 +++++++++- .../samples/refinhslot/SecondDerivedFoo.java | 70 ++++++-- .../samples/refinhslot/ThirdDerivedFoo.java | 152 ++++++++---------- .../linkml/schema/model/ClassDefinition.java | 95 +++++++++++ .../linkml/schema/model/Definition.java | 46 ++++-- .../linkml/schema/model/SlotDefinition.java | 95 +++++++++++ 10 files changed, 729 insertions(+), 269 deletions(-) diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Bar.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Bar.java index b9d0b28..2d8f4d5 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Bar.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Bar.java @@ -1,19 +1,74 @@ package org.incenp.linkml.core.samples.refinhslot; -/** - * An example of a class that is used in a “refined” slot. - *

- * This class is used in the {@link Foo} class. Some of the classes that are - * derived from Foo uses derived classes instead. - */ +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/Bar") public class Bar { + + @LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/name") private String name; + public void setName(String name) { + this.name = name; + } + public String getName() { - return name; + return this.name; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("Bar("); + if ( (o = this.getName()) != null ) { + sb.append("name="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof Bar) ) return false; + final Bar other = (Bar) o; + if ( !other.canEqual((Object) this)) return false; + final Object this$name = this.getName(); + final Object other$name = other.getName(); + if ( this$name == null ? other$name != null : !this$name.equals(other$name)) return false; + return true; + } + + protected boolean canEqual(final Object other) { + return other instanceof Bar; } - public void setName(String value) { - name = value; + @Override + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $name = this.getName(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + return result; } -} +} \ No newline at end of file diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedBar.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedBar.java index efe9acb..ada1f58 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedBar.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedBar.java @@ -1,20 +1,81 @@ package org.incenp.linkml.core.samples.refinhslot; -/** - * First derived class from Bar. - *

- * This class is used, instead of its parent Bar in - * {@link FirstDerivedFoo}. - */ +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/FirstDerivedBar") public class FirstDerivedBar extends Bar { - private int length; + @LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/length") + private Integer length; + + public void setLength(Integer length) { + this.length = length; + } + + public Integer getLength() { + return this.length; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("FirstDerivedBar("); + if ( (o = this.getLength()) != null ) { + sb.append("length="); + sb.append(o); + sb.append(","); + } + if ( (o = this.getName()) != null ) { + sb.append("name="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof FirstDerivedBar) ) return false; + final FirstDerivedBar other = (FirstDerivedBar) o; + if ( !other.canEqual((Object) this)) return false; + if ( !super.equals(o) ) return false; + + final Object this$length = this.getLength(); + final Object other$length = other.getLength(); + if ( this$length == null ? other$length != null : !this$length.equals(other$length)) return false; + return true; + } - public int getLength() { - return length; + protected boolean canEqual(final Object other) { + return other instanceof FirstDerivedBar; } - public void setLength(int value) { - length = value; + @Override + public int hashCode() { + final int PRIME = 59; + int result = super.hashCode(); + final Object $length = this.getLength(); + result = result * PRIME + ($length == null ? 43 : $length.hashCode()); + return result; } -} +} \ No newline at end of file diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedFoo.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedFoo.java index 8f1c450..8f403cf 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedFoo.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/FirstDerivedFoo.java @@ -1,33 +1,36 @@ package org.incenp.linkml.core.samples.refinhslot; +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; -/** - * An example of a class that refines the range of its slots to make them accept - * only a more specialised subclass. - */ +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/FirstDerivedFoo") public class FirstDerivedFoo extends Foo { - /* - * Overridden read accessor for the `bar` slot. - * - * We override it to ensure that it returns the more specialised subtype. - */ @Override public FirstDerivedBar getBar() { - // This cast is perfectly safe because the write accessor below guarantees that - // only a FirstDerivedBar object can be assigned to the slot. return (FirstDerivedBar) super.getBar(); } - /* - * Overridden write accessor for the `bar` slot. - * - * We override it to add a runtime check to enforce the more specialised type - * constraint. We cannot prevent client code from trying to assign an object of - * the wrong type, but if that happens we can at least immediately throw an - * exception. - */ + public void setBar(FirstDerivedBar value) { + super.setBar(value); + } + @Override public void setBar(Bar value) { if ( !(value instanceof FirstDerivedBar) ) { @@ -36,54 +39,17 @@ public void setBar(Bar value) { super.setBar(value); } - /* - * Overloaded write accessor for the `bar` slot. - * - * This accessor is not strictly necessary, but it makes it clearer that in this - * class, the value of the `bar` slot should be a `FirstDerivedBar`. It also - * allows to bypass the dynamic check in the normal accessor above, if the - * compiler already knows that the assigned value is a FirstDerivedBar. - */ - public void setBar(FirstDerivedBar value) { - super.setBar(value); - } - - /* - * Overridden “Standard” read accessor. - * - * We override it to ensure it returns the more specialised subtype. - * - * Because the slot could be (and instead is, in this example) refined further - * in subclasses, we must still return a generic wildcard, so this accessor has - * the same limitation as the one it overrides in the `Foo` class: modifying the - * returned list requires an explicit cast into a non-wildcard form. - */ @Override @SuppressWarnings("unchecked") public List getBars() { - // This cast should be safe IFF nobody explicitly modify the value returned by - // this accessor after casting it into a `List`. return (List) super.getBars(); } - /* - * Overridden read accessor with optional creation of the list. - * - * We must override this accessor to ensure that the created list (if the list - * needs to be created) is using the more specialised type. - */ @Override public List getBars(boolean create) { - // We can delegate the logic to the parent return super.getBars(FirstDerivedBar.class, create); } - /* - * Overridden parameterised read accessor. - * - * We must override this accessor to add a runtime check that the given type - * parameter is compatible with the more specialised type. - */ @Override public List getBars(Class t) { if ( !FirstDerivedBar.class.isAssignableFrom(t) ) { @@ -92,12 +58,6 @@ public List getBars(Class t) { return super.getBars(t); } - /* - * Overridden parameterised read accessor with optional creation of the list. - * - * Same as above: we must override this accessor to add a runtime check on the - * type parameter. - */ @Override public List getBars(Class t, boolean create) { if ( !FirstDerivedBar.class.isAssignableFrom(t) ) { @@ -106,20 +66,56 @@ public List getBars(Class t, boolean create) { return super.getBars(t, create); } - /* - * Overridden “standard” write accessor. - * - * We must override this accessor to include a runtime check. The check must be - * performed on all list items. - */ @Override public void setBars(List value) { - for ( Bar b : value ) { - if ( !(b instanceof FirstDerivedBar) ) { - throw new IllegalArgumentException("Invalid bars value"); + if ( value != null ) { + for ( Bar item : value ) { + if ( !(item instanceof FirstDerivedBar) ) { + throw new IllegalArgumentException("Invalid bars value"); + } } } super.setBars(value); } -} + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("FirstDerivedFoo("); + if ( (o = this.getBar()) != null ) { + sb.append("bar="); + sb.append(o); + sb.append(","); + } + if ( (o = this.getBars()) != null ) { + sb.append("bars="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof FirstDerivedFoo) ) return false; + final FirstDerivedFoo other = (FirstDerivedFoo) o; + if ( !other.canEqual((Object) this)) return false; + if ( !super.equals(o) ) return false; + + return true; + } + + protected boolean canEqual(final Object other) { + return other instanceof FirstDerivedFoo; + } + + @Override + public int hashCode() { + final int PRIME = 59; + int result = super.hashCode(); + return result; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Foo.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Foo.java index 7082bf6..8fc9d46 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Foo.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/Foo.java @@ -1,100 +1,115 @@ package org.incenp.linkml.core.samples.refinhslot; +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; -/** - * An example of a class whose slots are “refined” in derived classes. - */ +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/Foo") public class Foo { + @LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/bar") private Bar bar; - // We use a wildcard generic to allow derived classes to “refine” the parameter - // type + @LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/bars") private List bars; - /* - * Accessors for the `bar` slot. - * - * Nothing out of the ordinary here. - */ + public void setBar(Bar bar) { + this.bar = bar; + } + public Bar getBar() { - return bar; + return this.bar; } - public void setBar(Bar value) { - bar = value; + public void setBars(List bars) { + this.bars = bars; } - /* - * Accessors for the multi-valued `bars` slot. - */ - - /* - * “Standard” read accessor. Its return type is parameterized with a wildcard - * generic to allow derived classes to refine the parameter. - * - * Modifying the returned list is only possible by explicitly casting it to a - * non-wildcard form, as in: - * - * ((List) foo.getBars()).add(new Bar()); - * - * Without the cast, the following would be a compile-time error: - * - * foo.getBars().add(new Bar()); - */ public List getBars() { - return bars; + return this.bars; } - /* - * LinkML-Java “Standard” read accessor with optional creation of the list. - * - * This is a convenience accessor, intended to allow client code to dispense - * with a null-ness check. - * - * As for the argument-less read accessor, the return type is a wildcard, so - * modifying the returned list requires an explicit cast. - */ - public List getBars(boolean create) { - if ( bars == null && create ) { - bars = new ArrayList(); + public List getBars(boolean set) { + if ( this.bars == null && set ) { + this.bars = new ArrayList(); } - return bars; + return this.bars; } - /* - * Parameterised read accessor. - * - * This is another convenience accessor. This one is intended to allow client - * code to dispense with an explicit cast to modify the list: - * - * foo.getBars(Bar.class).add(new Bar()); - */ @SuppressWarnings("unchecked") public List getBars(Class t) { - return (List) bars; + return (List) this.bars; } - /* - * Parameterised read accessor with optional creation of the list. - * - * This is another convenience accessor, combining the effects of the two - * accessors above. - */ @SuppressWarnings("unchecked") public List getBars(Class t, boolean create) { - if ( bars == null && create ) { - bars = new ArrayList(); + if ( this.bars == null && create ) { + this.bars = new ArrayList(); + } + return (List) this.bars; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("Foo("); + if ( (o = this.getBar()) != null ) { + sb.append("bar="); + sb.append(o); + sb.append(","); } - return (List) bars; + if ( (o = this.getBars()) != null ) { + sb.append("bars="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof Foo) ) return false; + final Foo other = (Foo) o; + if ( !other.canEqual((Object) this)) return false; + final Object this$bar = this.getBar(); + final Object other$bar = other.getBar(); + if ( this$bar == null ? other$bar != null : !this$bar.equals(other$bar)) return false; + final Object this$bars = this.getBars(); + final Object other$bars = other.getBars(); + if ( this$bars == null ? other$bars != null : !this$bars.equals(other$bars)) return false; + return true; + } + + protected boolean canEqual(final Object other) { + return other instanceof Foo; } - /* - * “Standard” write accessor. - */ - public void setBars(List value) { - bars = value; + @Override + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $bar = this.getBar(); + result = result * PRIME + ($bar == null ? 43 : $bar.hashCode()); + final Object $bars = this.getBars(); + result = result * PRIME + ($bars == null ? 43 : $bars.hashCode()); + return result; } -} +} \ No newline at end of file diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedBar.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedBar.java index ec6a52c..bf0cc4c 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedBar.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedBar.java @@ -1,11 +1,86 @@ package org.incenp.linkml.core.samples.refinhslot; -/** - * Second derived class from Bar. - *

- * This class is used, instead of its parent FirstDerivedBar, in - * {@link ThirdDerivedFoo}. - */ +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/SecondDerivedBar") public class SecondDerivedBar extends FirstDerivedBar { -} + @LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/width") + private Integer width; + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getWidth() { + return this.width; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("SecondDerivedBar("); + if ( (o = this.getWidth()) != null ) { + sb.append("width="); + sb.append(o); + sb.append(","); + } + if ( (o = this.getLength()) != null ) { + sb.append("length="); + sb.append(o); + sb.append(","); + } + if ( (o = this.getName()) != null ) { + sb.append("name="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof SecondDerivedBar) ) return false; + final SecondDerivedBar other = (SecondDerivedBar) o; + if ( !other.canEqual((Object) this)) return false; + if ( !super.equals(o) ) return false; + + final Object this$width = this.getWidth(); + final Object other$width = other.getWidth(); + if ( this$width == null ? other$width != null : !this$width.equals(other$width)) return false; + return true; + } + + protected boolean canEqual(final Object other) { + return other instanceof SecondDerivedBar; + } + + @Override + public int hashCode() { + final int PRIME = 59; + int result = super.hashCode(); + final Object $width = this.getWidth(); + result = result * PRIME + ($width == null ? 43 : $width.hashCode()); + return result; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedFoo.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedFoo.java index 6ab2a54..67266b0 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedFoo.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/SecondDerivedFoo.java @@ -1,13 +1,65 @@ package org.incenp.linkml.core.samples.refinhslot; -/** - * An example of a class that inherits from a class that refines its slots, but - * that does no do any refinement itself. - */ +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/SecondDerivedFoo") public class SecondDerivedFoo extends FirstDerivedFoo { - /* - * In this class, the `bar` and `bars` slot are of the same type as in the - * parental `FirstDerivedFoo` class, so no overriding of accessors is necessary. - */ -} + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("SecondDerivedFoo("); + if ( (o = this.getBar()) != null ) { + sb.append("bar="); + sb.append(o); + sb.append(","); + } + if ( (o = this.getBars()) != null ) { + sb.append("bars="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof SecondDerivedFoo) ) return false; + final SecondDerivedFoo other = (SecondDerivedFoo) o; + if ( !other.canEqual((Object) this)) return false; + if ( !super.equals(o) ) return false; + + return true; + } + + protected boolean canEqual(final Object other) { + return other instanceof SecondDerivedFoo; + } + + @Override + public int hashCode() { + final int PRIME = 59; + int result = super.hashCode(); + return result; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/ThirdDerivedFoo.java b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/ThirdDerivedFoo.java index e06104a..89c3f52 100644 --- a/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/ThirdDerivedFoo.java +++ b/core/src/test/java/org/incenp/linkml/core/samples/refinhslot/ThirdDerivedFoo.java @@ -1,48 +1,36 @@ package org.incenp.linkml.core.samples.refinhslot; +import java.net.URI; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; -/** - * An example of a class that refines the range of its slots, that it inherited - * from a class that already refined them. - * - * Importantly, this class has no derived class, so we know its slots cannot be - * further refined by another class. - */ +import org.incenp.linkml.core.annotations.Converter; +import org.incenp.linkml.core.annotations.ExtensionHolder; +import org.incenp.linkml.core.annotations.Identifier; +import org.incenp.linkml.core.annotations.Inlined; +import org.incenp.linkml.core.annotations.LinkURI; +import org.incenp.linkml.core.annotations.Required; +import org.incenp.linkml.core.annotations.SlotName; +import org.incenp.linkml.core.annotations.TypeDesignator; +import org.incenp.linkml.core.CurieConverter; + +@LinkURI("https://w3id.org/linkml/tests/refined_derived_slots/ThirdDerivedFoo") public class ThirdDerivedFoo extends SecondDerivedFoo { - /* - * Overridden read accessor for the `bar` slot. - * - * We override it to ensure that it returns the more specialised subtype. - */ @Override public SecondDerivedBar getBar() { return (SecondDerivedBar) super.getBar(); } - /* - * Overridden write accessor for the `bar` slot. - * - * We override it to add a runtime check to enforce the more specialised type - * constraint. We cannot prevent client code from trying to assign an object of - * the wrong type, but if that happens we can at least immediately throw an - * exception. - */ - @Override - public void setBar(Bar value) { - if ( !(value instanceof SecondDerivedBar) ) { - throw new IllegalArgumentException("Invalid bar value"); - } + public void setBar(SecondDerivedBar value) { super.setBar(value); } - /* - * Second overridden write accessor for the `bar` slot. - * - * Since `FirstDerivedFoo` defined this accessor, we must override it as well, - * otherwise it would allow client code to assign a FirstDerivedBar to the slot. - */ @Override public void setBar(FirstDerivedBar value) { if ( !(value instanceof SecondDerivedBar) ) { @@ -51,52 +39,25 @@ public void setBar(FirstDerivedBar value) { super.setBar(value); } - /* - * Overloaded write accessor for the `bar` slot. - * - * This accessor is not strictly necessary, but it makes it clearer that in this - * class, the value of the `bar` slot should be a `SecondDerivedBar`. It also - * allows to bypass the dynamic check in the normal accessor above, if the - * compiler already knows that the assigned value is a FirstDerivedBar. - */ - public void setBar(SecondDerivedBar value) { + @Override + public void setBar(Bar value) { + if ( !(value instanceof SecondDerivedBar) ) { + throw new IllegalArgumentException("Invalid bar value"); + } super.setBar(value); } - /* - * Overridden “standard” read accessor. - * - * We override it to ensure it returns the more specialised subtype. - * - * Here, since we know the slot cannot be further refined (no subclass), we can - * dispense with a wildcard generic. - */ @Override @SuppressWarnings("unchecked") public List getBars() { return (List) super.getBars(); } - /* - * Overriden read accessor with optional creation of the list. - * - * We must override this accessor to ensure that the created list (if the list - * needs to be created) is using the more specialised type. - */ @Override public List getBars(boolean create) { - // We can delegate the logic to the parent return super.getBars(SecondDerivedBar.class, create); } - /* - * Overidden parameterised read accessor. - * - * In this class, we don’t need this accessor to get a modifiable list (we can - * use `getBars()` directly), but we must still override the accessor we inherit - * from the parent, otherwise this would allow client code to get a - * `List`-typed value. - */ @Override public List getBars(Class t) { if ( !SecondDerivedBar.class.isAssignableFrom(t) ) { @@ -105,12 +66,6 @@ public List getBars(Class t) { return super.getBars(t); } - /* - * Overridden parameterised read accessor with optional creation of the list. - * - * Same as above: we must override this accessor to add a runtime check on the - * type parameter. - */ @Override public List getBars(Class t, boolean create) { if ( !SecondDerivedBar.class.isAssignableFrom(t) ) { @@ -119,21 +74,56 @@ public List getBars(Class t, boolean create) { return super.getBars(t, create); } - /* - * Overridden “standard” write accessor. - * - * We must override this accessor to include a runtime check. The check must be - * performed on all list items. - */ @Override public void setBars(List value) { - for ( Bar b : value ) { - if ( !(b instanceof SecondDerivedBar) ) { - throw new IllegalArgumentException("Invalid bar value"); + if ( value != null ) { + for ( Bar item : value ) { + if ( !(item instanceof SecondDerivedBar) ) { + throw new IllegalArgumentException("Invalid bars value"); + } } } - // FIXME: the parent method will in turn perform a (no longer needed) runtime - // check... super.setBars(value); } -} + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + Object o; + sb.append("ThirdDerivedFoo("); + if ( (o = this.getBar()) != null ) { + sb.append("bar="); + sb.append(o); + sb.append(","); + } + if ( (o = this.getBars()) != null ) { + sb.append("bars="); + sb.append(o); + sb.append(","); + } + sb.append(")"); + return sb.toString(); + } + + @Override + public boolean equals(final Object o) { + if ( o == this ) return true; + if ( !(o instanceof ThirdDerivedFoo) ) return false; + final ThirdDerivedFoo other = (ThirdDerivedFoo) o; + if ( !other.canEqual((Object) this)) return false; + if ( !super.equals(o) ) return false; + + return true; + } + + protected boolean canEqual(final Object other) { + return other instanceof ThirdDerivedFoo; + } + + @Override + public int hashCode() { + final int PRIME = 59; + int result = super.hashCode(); + return result; + } +} \ No newline at end of file diff --git a/ext/src/main/java/org/incenp/linkml/schema/model/ClassDefinition.java b/ext/src/main/java/org/incenp/linkml/schema/model/ClassDefinition.java index 3f21432..fe521b8 100644 --- a/ext/src/main/java/org/incenp/linkml/schema/model/ClassDefinition.java +++ b/ext/src/main/java/org/incenp/linkml/schema/model/ClassDefinition.java @@ -388,6 +388,101 @@ public List getSlotConditions(boolean set) { return this.slotConditions; } + @Override + public ClassDefinition getIsA() { + return (ClassDefinition) super.getIsA(); + } + + public void setIsA(ClassDefinition value) { + super.setIsA(value); + } + + @Override + public void setIsA(Definition value) { + if ( !(value instanceof ClassDefinition) ) { + throw new IllegalArgumentException("Invalid isA value"); + } + super.setIsA(value); + } + + @Override + @SuppressWarnings("unchecked") + public List getMixins() { + return (List) super.getMixins(); + } + + @Override + public List getMixins(boolean create) { + return super.getMixins(ClassDefinition.class, create); + } + + @Override + public List getMixins(Class t) { + if ( !ClassDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getMixins(t); + } + + @Override + public List getMixins(Class t, boolean create) { + if ( !ClassDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getMixins(t, create); + } + + @Override + public void setMixins(List value) { + if ( value != null ) { + for ( Definition item : value ) { + if ( !(item instanceof ClassDefinition) ) { + throw new IllegalArgumentException("Invalid mixins value"); + } + } + } + super.setMixins(value); + } + + @Override + @SuppressWarnings("unchecked") + public List getApplyTo() { + return (List) super.getApplyTo(); + } + + @Override + public List getApplyTo(boolean create) { + return super.getApplyTo(ClassDefinition.class, create); + } + + @Override + public List getApplyTo(Class t) { + if ( !ClassDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getApplyTo(t); + } + + @Override + public List getApplyTo(Class t, boolean create) { + if ( !ClassDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getApplyTo(t, create); + } + + @Override + public void setApplyTo(List value) { + if ( value != null ) { + for ( Definition item : value ) { + if ( !(item instanceof ClassDefinition) ) { + throw new IllegalArgumentException("Invalid applyTo value"); + } + } + } + super.setApplyTo(value); + } + @Override public String toString() { return "ClassDefinition(name=" + this.getName() + ")"; diff --git a/ext/src/main/java/org/incenp/linkml/schema/model/Definition.java b/ext/src/main/java/org/incenp/linkml/schema/model/Definition.java index caeafbd..8f6e86c 100644 --- a/ext/src/main/java/org/incenp/linkml/schema/model/Definition.java +++ b/ext/src/main/java/org/incenp/linkml/schema/model/Definition.java @@ -34,11 +34,11 @@ public abstract class Definition extends Element { private Boolean mixin; @LinkURI("https://w3id.org/linkml/mixins") - private List mixins; + private List mixins; @SlotName("apply_to") @LinkURI("https://w3id.org/linkml/apply_to") - private List applyTo; + private List applyTo; @SlotName("values_from") @Converter(CurieConverter.class) @@ -73,36 +73,62 @@ public Boolean getMixin() { return this.mixin; } - public void setMixins(List mixins) { + public void setMixins(List mixins) { this.mixins = mixins; } - public List getMixins() { + public List getMixins() { return this.mixins; } - public List getMixins(boolean set) { + public List getMixins(boolean set) { if ( this.mixins == null && set ) { - this.mixins = new ArrayList<>(); + this.mixins = new ArrayList(); } return this.mixins; } - public void setApplyTo(List applyTo) { + @SuppressWarnings("unchecked") + public List getMixins(Class t) { + return (List) this.mixins; + } + + @SuppressWarnings("unchecked") + public List getMixins(Class t, boolean create) { + if ( this.mixins == null && create ) { + this.mixins = new ArrayList(); + } + return (List) this.mixins; + } + + public void setApplyTo(List applyTo) { this.applyTo = applyTo; } - public List getApplyTo() { + public List getApplyTo() { return this.applyTo; } - public List getApplyTo(boolean set) { + public List getApplyTo(boolean set) { if ( this.applyTo == null && set ) { - this.applyTo = new ArrayList<>(); + this.applyTo = new ArrayList(); } return this.applyTo; } + @SuppressWarnings("unchecked") + public List getApplyTo(Class t) { + return (List) this.applyTo; + } + + @SuppressWarnings("unchecked") + public List getApplyTo(Class t, boolean create) { + if ( this.applyTo == null && create ) { + this.applyTo = new ArrayList(); + } + return (List) this.applyTo; + } + public void setValuesFrom(List valuesFrom) { this.valuesFrom = valuesFrom; } diff --git a/ext/src/main/java/org/incenp/linkml/schema/model/SlotDefinition.java b/ext/src/main/java/org/incenp/linkml/schema/model/SlotDefinition.java index 78b5520..ad07b4f 100644 --- a/ext/src/main/java/org/incenp/linkml/schema/model/SlotDefinition.java +++ b/ext/src/main/java/org/incenp/linkml/schema/model/SlotDefinition.java @@ -873,6 +873,101 @@ public ArrayExpression getArray() { return this.array; } + @Override + public SlotDefinition getIsA() { + return (SlotDefinition) super.getIsA(); + } + + public void setIsA(SlotDefinition value) { + super.setIsA(value); + } + + @Override + public void setIsA(Definition value) { + if ( !(value instanceof SlotDefinition) ) { + throw new IllegalArgumentException("Invalid isA value"); + } + super.setIsA(value); + } + + @Override + @SuppressWarnings("unchecked") + public List getMixins() { + return (List) super.getMixins(); + } + + @Override + public List getMixins(boolean create) { + return super.getMixins(SlotDefinition.class, create); + } + + @Override + public List getMixins(Class t) { + if ( !SlotDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getMixins(t); + } + + @Override + public List getMixins(Class t, boolean create) { + if ( !SlotDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getMixins(t, create); + } + + @Override + public void setMixins(List value) { + if ( value != null ) { + for ( Definition item : value ) { + if ( !(item instanceof SlotDefinition) ) { + throw new IllegalArgumentException("Invalid mixins value"); + } + } + } + super.setMixins(value); + } + + @Override + @SuppressWarnings("unchecked") + public List getApplyTo() { + return (List) super.getApplyTo(); + } + + @Override + public List getApplyTo(boolean create) { + return super.getApplyTo(SlotDefinition.class, create); + } + + @Override + public List getApplyTo(Class t) { + if ( !SlotDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getApplyTo(t); + } + + @Override + public List getApplyTo(Class t, boolean create) { + if ( !SlotDefinition.class.isAssignableFrom(t) ) { + throw new IllegalArgumentException("Invalid type parameter"); + } + return super.getApplyTo(t, create); + } + + @Override + public void setApplyTo(List value) { + if ( value != null ) { + for ( Definition item : value ) { + if ( !(item instanceof SlotDefinition) ) { + throw new IllegalArgumentException("Invalid applyTo value"); + } + } + } + super.setApplyTo(value); + } + @Override public String toString() { return "SlotDefinition(name=" + this.getName() + ")";