You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2023/06/25 16:30:45 UTC

[causeway] branch master updated: CAUSEWAY-3395 : updates user guide, value types

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new d36590352a CAUSEWAY-3395 : updates user guide, value types
d36590352a is described below

commit d36590352ac4a807ca8a4e501f3c0958ca7724a9
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Jun 25 17:29:31 2023 +0100

    CAUSEWAY-3395 : updates user guide, value types
---
 .../userguide/modules/ROOT/pages/value-types.adoc  | 111 ++++++++++++++++-----
 1 file changed, 86 insertions(+), 25 deletions(-)

diff --git a/antora/components/userguide/modules/ROOT/pages/value-types.adoc b/antora/components/userguide/modules/ROOT/pages/value-types.adoc
index 9c406ae56f..7c021e8018 100644
--- a/antora/components/userguide/modules/ROOT/pages/value-types.adoc
+++ b/antora/components/userguide/modules/ROOT/pages/value-types.adoc
@@ -266,7 +266,7 @@ public class EmailAddressValueSemantics
 
     @Override
     public ValueType getSchemaValueType() {
-        return ValueType.STRING;
+        return ValueType.STRING;                                            // <.>
     }
 
     @Override
@@ -353,6 +353,7 @@ public class EmailAddressValueSemantics
     }
 }
 ----
+<.> determines the UI widget that the framework uses to display/edit the value
 <.> the `compose()` and `decompose()` methods are used to serialize the object using the structures defined by the link:https://causeway.apache.org/refguide/2.0.0-RC1/schema/about.html[XSD schemas].
 +
 Using this, the framework can render the composite value as JSON (as used by the REST API), or to XML, as used by SPIs such as xref:refguide:applib:index/services/publishing/spi/CommandSubscriber.adoc[CommandSubscriber] (see xref:refguide:applib:index/services/command/Command.adoc[Command] and xref:refguide:schema:cmd.adoc[CommandDto]).
@@ -455,12 +456,14 @@ public class DateInterval {
 <.> It's common for value types to have a set of methods that act upon them (sometimes called an "algebra").
 <.> Internally we leverage Joda to do the heavy lifting.
 
+The value type can be used in entities and view models the same as any other value type.
+For example:
 
 [source,java]
 ----
 // ..
 @DomainObject(nature=Nature.ENTITY)
-public class CalendarEntry {
+public class CarRental {
 
     @Property(editing = Editing.ENABLED)
     @Getter @Setter
@@ -469,16 +472,19 @@ public class CalendarEntry {
 }
 ----
 
+As with scalar custom types, we need some glue to "teach" the framework how to render with the value type, though it works slightly differently; rather than parsing text input to set the value, instead we provide a special mixin that the framework uses to prompt for the constituent values.
+The name of this mixin is always called "default".
+
+For our `DateInterval` example:
 
-The "default" mixin:
 
 [source,java]
 .DateInterval_default.java
 ----
 @Action(semantics = SemanticsOf.SAFE)
-@ActionLayout(promptStyle = PromptStyle.INLINE_AS_IF_EDIT)  // <.>
+@ActionLayout(promptStyle = PromptStyle.INLINE_AS_IF_EDIT)          // <.>
 @RequiredArgsConstructor
-public class DateInterval_default {
+public class DateInterval_default {                                 // <.>
 
     private final DateInterval mixee;
 
@@ -488,15 +494,26 @@ public class DateInterval_default {
     ) {
         return DateInterval.of(startDate, endDate);
     }
-    @MemberSupport public LocalDate defaultRe() {
-        return mixee.getRe();
+    @MemberSupport public LocalDate defaultStartDate() {
+        return mixee.getStartDate();
+    }
+    @MemberSupport public LocalDate defaultEndDate() {
+        return mixee.getEndDate();
     }
-    @MemberSupport public LocalDate defaultIm() {
-        return mixee.getIm();
+    @MemberSupport public LocalDate validateAct(                    // <.>
+            final LocalDate startDate,
+            final LocalDate endDate) {
+        return startDate.isBefore(endDate)
+                    ? null
+                    : "Start date must be before the end date";
     }
 }
 ----
+<.> The "default" action _must_ use this prompt style
+<.> Must be named "default"
+<.> Enforces validation constraints
 
+In addition, we also need an implementation of the xref:refguide:applib:index/value/semantics/ValueSemanticsProvider.adoc[ValueSemanticsProvider] SPI:
 
 [source,java]
 .DateIntervalValueSemantics.java
@@ -504,23 +521,26 @@ public class DateInterval_default {
 @Named("demo.DateIntervalValueSemantics")
 @Component
 @Import({
-        DateInterval_default.class
+        DateInterval_default.class                                          // <.>
 })
+@RequiredArgsConstructor(onConstructor_ = { @Inject })
 public class DateIntervalValueSemantics
         extends ValueSemanticsAbstract<DateInterval> {
 
+    final ClockService clockService;
+
     @Override
     public Class<DateInterval> getCorrespondingClass() {
         return DateInterval.class;
     }
 
     @Override
-    public ValueType getSchemaValueType() {
+    public ValueType getSchemaValueType() {                                 // <.>
         return ValueType.COMPOSITE;
     }
 
     @Override
-    public ValueDecomposition decompose(final DateInterval value) {
+    public ValueDecomposition decompose(final DateInterval value) {         // <.>
         return CommonDtoUtils.typedTupleBuilder(value)
                 .addFundamentalType(ValueType.LOCAL_DATE, "startDate", DateInterval::getStartDate)
                 .addFundamentalType(ValueType.LOCAL_DATE, "endDate", DateInterval::getEndDate)
@@ -528,7 +548,7 @@ public class DateIntervalValueSemantics
     }
 
     @Override
-    public DateInterval compose(final ValueDecomposition decomposition) {
+    public DateInterval compose(final ValueDecomposition decomposition) {   // <3>
         return decomposition.right()
                 .map(CommonDtoUtils::typedTupleAsMap)
                 .map(map-> DateInterval.of(
@@ -538,35 +558,76 @@ public class DateIntervalValueSemantics
     }
 
     @Override
-    public DefaultsProvider<DateInterval> getDefaultsProvider() {
-        return ()-> DateInterval.of(0, 0);
+    public DefaultsProvider<DateInterval> getDefaultsProvider() {           // <.>
+        val nowAsMilli = clockService.getClock().now().toEpochMilli();
+        val now = new org.joda.time.DateTime(nowAsMilli).toLocalDate();
+        return ()-> DateInterval.of(now, now.plusDays(7));
     }
 
     @Override
-    public Renderer<DateInterval> getRenderer() {
+    public Renderer<DateInterval> getRenderer() {                           // <.>
         return new Renderer<>() {
             @Override
             public String titlePresentation(Context context, DateInterval object) {
-                if (object == null) return "NaN";
-                return object.getRe() +
-                        (object.getIm() >= 0
-                                ? (" + " +  object.getIm())
-                                : (" - " + (-object.getIm())))
-                        + "i";
+                if (object == null) return "(none)";
+                return "[" + object.getStartDate() + ", " + object.getEndDate() + "]";
             }
         };
     }
 }
 ----
+<.> Declares the existence of the "default" mixin.
+<.> Indicates this is a composite, and therefore that the value should be manipulated in the UI by way of the "default" mixin's prompt
+<.> the `compose()` and `decompose()` methods are used to serialize the object using the structures defined by the link:https://causeway.apache.org/refguide/2.0.0-RC1/schema/about.html[XSD schemas].
++
+Using this, the framework can render the composite value as JSON (as used by the REST API), or to XML, as used by SPIs such as xref:refguide:applib:index/services/publishing/spi/CommandSubscriber.adoc[CommandSubscriber] (see xref:refguide:applib:index/services/command/Command.adoc[Command] and xref:refguide:schema:cmd.adoc[CommandDto]).
+<.> the `getDefaultsProvider()` provides an initial value (eg non-nullable properties)
+<.> the `getRenderer()` is used to render the value as a string.
+An HTML representation can also be provided, though this type doesn’t warrant one.
+
+Compared to scalar types, note that the `ValueSemanticsProvider` does not need to provide an implementation of `getParser()` - instead the "default" mixin does this work.
+Also, it is not possible to use a custom value type as part of the object's id, and so no implementation of `getIdStringifier()` is required either.
 
 
-ORM mapping concerns:
+If using within the database then you will also need to map the custom type to the database:
 
 * if mapping to xref:pjpa::about.adoc[JPA/EclipseLink], use `@Embedded` and `@Embeddable`; see for example link:https://www.baeldung.com/jpa-embedded-embeddable[this baeldung post] on the topic.
 * If mapping to xref:pjdo::about.adoc[JDO/DataNucleus], use `@Embedded` and `@PersistenceCapable(embeddedOnly="true")`; see https://www.datanucleus.org/products/accessplatform_6_0/jdo/mapping.html#embedded[Datanucleus documentation].
 
 
 
-== 3rd party library
+// == 3rd party library
+//
+// TODO: show JScience temperature, say.
+
+// import javax.measure.Quantity;
+// import javax.measure.quantity.Temperature;
+// import tech.units.indriya.quantity.Quantities;
+// import tech.units.indriya.unit.Units;
+//
+// public class TemperatureConversionExample {
+// public static void main(String[] args) {
+// // Create a Quantity representing a temperature of 25 degrees Celsius
+// Quantity<Temperature> celsius = Quantities.getQuantity(25, Units.CELSIUS);
+//
+//         // Convert Celsius to Fahrenheit
+//         Quantity<Temperature> fahrenheit = celsius.to(Units.FAHRENHEIT);
+//         System.out.println("Temperature in Fahrenheit: " + fahrenheit);
+//
+//         // Convert Celsius to Kelvin
+//         Quantity<Temperature> kelvin = celsius.to(Units.KELVIN);
+//         System.out.println("Temperature in Kelvin: " + kelvin);
+//
+//         // Create a Quantity representing a temperature of 68 degrees Fahrenheit
+//         Quantity<Temperature> fahrenheit2 = Quantities.getQuantity(68, Units.FAHRENHEIT);
+//
+//         // Convert Fahrenheit to Celsius
+//         Quantity<Temperature> celsius2 = fahrenheit2.to(Units.CELSIUS);
+//         System.out.println("Temperature in Celsius: " + celsius2);
+//
+//         // Convert Fahrenheit to Kelvin
+//         Quantity<Temperature> kelvin2 = fahrenheit2.to(Units.KELVIN);
+//         System.out.println("Temperature in Kelvin: " + kelvin2);
+//     }
+// }
 
-TODO: show JScience temperature, say.