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

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

commit a477d7515f7f257e3bb0788cf701d8cb9507e9eb
Author: danhaywood <[email protected]>
AuthorDate: Thu May 11 07:34:52 2023 +0100

    CAUSEWAY-2485: adds example of scalar value type
---
 .../ComplexNumberValueSemantics.java               |  37 +++--
 .../CompositeValueTypePage-description.adoc        |  42 +++---
 .../progmodel/customvalues/EmailAddress.java       |  29 ++++
 .../customvalues/EmailAddressValueSemantics.java   | 154 +++++++++++++++++++++
 .../objects/progmodel/customvalues/Percentage.java |  29 ++++
 .../customvalues/ScalarValueTypeMenu.java          |  48 +++++++
 .../ScalarValueTypePage-description.adoc           | 104 ++++++++++++++
 .../customvalues/ScalarValueTypePage.java          |  50 +++++++
 .../customvalues/ScalarValueTypePage.layout.xml    |  60 ++++++++
 .../src/main/java/demoapp/dom/menubars.layout.xml  |   1 +
 .../src/main/resources/static/css/application.css  |   2 +-
 11 files changed, 519 insertions(+), 37 deletions(-)

diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/ComplexNumberValueSemantics.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/ComplexNumberValueSemantics.java
index 9a259a458e..3d61644b25 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/ComplexNumberValueSemantics.java
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/ComplexNumberValueSemantics.java
@@ -21,7 +21,6 @@ package demoapp.dom.domain.objects.progmodel.compositevalues;
 import javax.inject.Named;
 
 import org.springframework.context.annotation.Import;
-import org.springframework.context.annotation.Profile;
 import org.springframework.stereotype.Component;
 
 import org.apache.causeway.applib.util.schema.CommonDtoUtils;
@@ -51,13 +50,6 @@ public class ComplexNumberValueSemantics
         return ValueType.COMPOSITE;
     }
 
-// tag::getDefaultsProvider[]
-    @Override
-    public DefaultsProvider<ComplexNumber> getDefaultsProvider() {
-        return ()-> ComplexNumber.of(0, 0);
-    }
-// end::getDefaultsProvider[]
-
 // tag::compose[]
     @Override
     public ValueDecomposition decompose(final ComplexNumber value) {
@@ -78,21 +70,28 @@ public class ComplexNumberValueSemantics
     }
 // end::compose[]
 
-// tag::getRenderer[]
+// tag::getDefaultsProvider[]
     @Override
-    public Renderer<ComplexNumber> getRenderer() {
-        return (context, object) -> title(object, "NaN");
+    public DefaultsProvider<ComplexNumber> getDefaultsProvider() {
+        return ()-> ComplexNumber.of(0, 0);
     }
+// end::getDefaultsProvider[]
 
-    private static String title(ComplexNumber complexNumber, final String 
fallbackIfNull) {
-        if (complexNumber == null) return fallbackIfNull;
-        return complexNumber.getRe() +
-                (complexNumber.getIm() >= 0
-                        ? (" + " +  complexNumber.getIm())
-                        : (" - " + (-complexNumber.getIm())))
-                + "i";
+// tag::getRenderer[]
+    @Override
+    public Renderer<ComplexNumber> getRenderer() {
+        return new Renderer<>() {
+            @Override
+            public String titlePresentation(Context context, ComplexNumber 
object) {
+                if (object == null) return "NaN";
+                return object.getRe() +
+                        (object.getIm() >= 0
+                                ? (" + " +  object.getIm())
+                                : (" - " + (-object.getIm())))
+                        + "i";
+            }
+        };
     }
-
 // end::getRenderer[]
 
 // tag::class[]
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/CompositeValueTypePage-description.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/CompositeValueTypePage-description.adoc
index 5094cdf96a..012bb58185 100644
--- 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/CompositeValueTypePage-description.adoc
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/compositevalues/CompositeValueTypePage-description.adoc
@@ -8,7 +8,8 @@ Value types are distinct from "entity types," which represent 
objects with a uni
 In contrast, value types have no identity of their own, and two value objects 
with the same values are considered equal.
 It's also common for value types to define an algebra, where they can be 
combined through operations to produce new values.
 
-A composite value type has more than one data attribute.
+A scalar value type has a single data attribute, while a composite value type 
has multiple.
+This page demonstrates the use of *composite* value types.
 
 == How this demo works
 
@@ -57,46 +58,47 @@ WARNING: the framework currently has a bug in that it 
doesn't prevent the end-us
 +
 The action to subtract is similar.
 
+=== Value Semantics Provider
+
 The framework also needs to know the structure of the value type.
 This is done by providing an implementation of a 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/value/semantics/ValueSemanticsProvider.html[ValueSemanticsProvider].
 
 * the `ValueSemanticsProvider` implementation is:
 +
-[source,java]
+[source,java,indent=0]
 .ComplexNumberValueSemantics.java
 ----
 include::ComplexNumberValueSemantics.java[tags=class]
 ----
-<.> declares a mixin that the framework uses to present an action prompt to 
set the parts of the value type.
+<.> declares a "default" mixin that the framework uses to present an action 
prompt to set the parts of the value type.
 This is discussed in more detail below.
 <.> inherits from 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/value/semantics/ValueSemanticsAbstract.html[ValueSemanticsAbstract],
 a convenience adapter.
 
-* its implementation of `getDefaultsProvider()` is:
+* 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].
 +
-[source,java]
-.ComplexNumberValueSemantics.java
-----
-include::ComplexNumberValueSemantics.java[tags=getDefaultsProvider]
-----
-
-* its implementation of `compose()` and `decompose()` is:
+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 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/services/publishing/spi/CommandSubscriber.html#section-top[CommandSubscriber]
 (see 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/services/command/Command.html[Command]
 and 
link:https://causeway.apache.org/refguide/2.0.0-RC1/schema/cmd.html[CommandDto]).
 +
-[source,java]
-.ComplexNumberValueSemantics.java
+[source,java,indent=0]
 ----
 include::ComplexNumberValueSemantics.java[tags=compose]
 ----
 
-* its implementation of `getRenderer()` is:
+* 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.
 +
-[source,java]
-.ComplexNumberValueSemantics.java
+[source,java,indent=0]
 ----
 include::ComplexNumberValueSemantics.java[tags=getRenderer]
 ----
 
-* As noted above, the "default" mixin defines an action to set the parts of 
the value type.
+* the `getDefaultsProvider()` provides an initial value (eg non-nullable 
properties):
 +
+[source,java,indent=0]
+----
+include::ComplexNumberValueSemantics.java[tags=getDefaultsProvider]
+----
+
+* Finally, the "default" mixin (mentioned above) defines an action to set the 
components of the value type.
 The name "default" in this context is in effect a reserved method name.
 +
 [source,java]
@@ -106,4 +108,10 @@ include::ComplexNumber_default.java[tags=default-mixin]
 ----
 <.> should always specify the `INLINE_AS_IF_EDIT` prompt style, because the 
prompt may be "nested" within an outer action prompt (e.g. the 
`addComplexNumber` action of the page object iself)
 
+Comparing composite value types to scalar value types, you'll notice that a 
composite type doesn't need to define a `Parser` implementation; instead the 
"default" mixin performs an equivalent role.
+
+=== Integrating with an ORM
+
+Be aware that if using the value type within an entity, then it will also be 
necessary to implement additional code to instruct the ORM how to convert the 
value type into the database.
 
+The "embedded types" demo page shows how this can be done.
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/EmailAddress.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/EmailAddress.java
new file mode 100644
index 0000000000..f67333b673
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/EmailAddress.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *        http://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 demoapp.dom.domain.objects.progmodel.customvalues;
+
+
+// tag::class[]
[email protected]        // <.>
[email protected]                                       // <.>
[email protected](staticName = "of")
+public class EmailAddress {
+    String emailAddress;                            // <.>
+}
+// end::class[]
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/EmailAddressValueSemantics.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/EmailAddressValueSemantics.java
new file mode 100644
index 0000000000..1e639613b3
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/EmailAddressValueSemantics.java
@@ -0,0 +1,154 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *        http://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 demoapp.dom.domain.objects.progmodel.customvalues;
+
+import lombok.NonNull;
+
+import java.util.regex.Pattern;
+
+import javax.inject.Named;
+
+import org.apache.causeway.applib.services.bookmark.IdStringifier;
+import org.apache.causeway.applib.value.semantics.Parser;
+import org.apache.causeway.commons.internal.base._Strings;
+import org.apache.causeway.schema.common.v2.ValueWithTypeDto;
+
+import org.springframework.stereotype.Component;
+
+import org.apache.causeway.applib.value.semantics.DefaultsProvider;
+import org.apache.causeway.applib.value.semantics.Renderer;
+import org.apache.causeway.applib.value.semantics.ValueDecomposition;
+import org.apache.causeway.applib.value.semantics.ValueSemanticsAbstract;
+import org.apache.causeway.schema.common.v2.ValueType;
+
+// tag::class[]
+@Named("demo.EmailAddressValueSemantics")
+@Component
+public class EmailAddressValueSemantics
+        extends ValueSemanticsAbstract<EmailAddress> {   // <.>
+    // ...
+// end::class[]
+
+    @Override
+    public Class<EmailAddress> getCorrespondingClass() {
+        return EmailAddress.class;
+    }
+
+    @Override
+    public ValueType getSchemaValueType() {
+        return ValueType.STRING;
+    }
+
+// tag::compose[]
+    @Override
+    public ValueDecomposition decompose(final EmailAddress value) {
+        return decomposeAsNullable(value, EmailAddress::getEmailAddress, 
()->null);
+    }
+
+    @Override
+    public EmailAddress compose(final ValueDecomposition decomposition) {
+        return composeFromNullable(
+                decomposition, ValueWithTypeDto::getString, EmailAddress::of, 
()->null);
+    }
+// end::compose[]
+
+// tag::getDefaultsProvider[]
+    @Override
+    public DefaultsProvider<EmailAddress> getDefaultsProvider() {
+        return new DefaultsProvider<EmailAddress>() {
+            @Override
+            public EmailAddress getDefaultValue() {
+                return EmailAddress.of("");
+            }
+        };
+    }
+// end::getDefaultsProvider[]
+
+// tag::getRenderer[]
+    @Override
+    public Renderer<EmailAddress> getRenderer() {
+        return new Renderer<>() {
+            @Override
+            public String titlePresentation(Context context, EmailAddress 
emailAddress) {
+                return emailAddress == null ? null : 
emailAddress.getEmailAddress();
+            }
+        };
+    }
+
+// end::getRenderer[]
+
+// tag::getParser[]
+    @Override
+    public Parser<EmailAddress> getParser() {
+        return new Parser<>() {
+            // https://stackoverflow.com/a/47181151
+            final Pattern REGEX = 
Pattern.compile("^[\\w-\\+]+(\\.[\\w]+)*@[\\w-]+(\\.[\\w]+)*(\\.[a-zA-Z]{2,})$");
+
+            @Override
+            public String parseableTextRepresentation(Context context, 
EmailAddress value) {
+                return renderTitle(value, EmailAddress::getEmailAddress);
+            }
+
+            @Override
+            public EmailAddress parseTextRepresentation(Context context, 
String text) {
+                if(!REGEX.matcher(text).matches()) {
+                    throw new RuntimeException("Invalid email format");
+                }
+                if (_Strings.isEmpty(text)) return null;
+                return EmailAddress.of(text);
+            }
+
+            @Override
+            public int typicalLength() {
+                return 20;
+            }
+
+            @Override
+            public int maxLength() {
+                return 50;
+            }
+        };
+    }
+// end::getParser[]
+
+// tag::getIdStringifier[]
+    @Override
+    public IdStringifier<EmailAddress> getIdStringifier() {
+        return new IdStringifier.EntityAgnostic<>() {
+            @Override
+            public Class<EmailAddress> getCorrespondingClass() {
+                return EmailAddressValueSemantics.this.getCorrespondingClass();
+            }
+
+            @Override
+            public String enstring(@NonNull EmailAddress value) {
+                return _Strings.base64UrlEncode(value.getEmailAddress());
+            }
+
+            @Override
+            public EmailAddress destring(@NonNull String stringified) {
+                return EmailAddress.of(_Strings.base64UrlDecode(stringified));
+            }
+        };
+    }
+// end::getIdStringifier[]
+
+// tag::class[]
+}
+// end::class[]
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/Percentage.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/Percentage.java
new file mode 100644
index 0000000000..883bf94322
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/Percentage.java
@@ -0,0 +1,29 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *        http://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 demoapp.dom.domain.objects.progmodel.customvalues;
+
+
+// tag::class[]
[email protected]        // <.>
[email protected]                                       // <.>
[email protected](staticName = "of")
+public class Percentage {
+    int percentage;                                 // <.>
+}
+// end::class[]
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypeMenu.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypeMenu.java
new file mode 100644
index 0000000000..ad454dcabb
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypeMenu.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *        http://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 demoapp.dom.domain.objects.progmodel.customvalues;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.causeway.applib.annotation.Action;
+import org.apache.causeway.applib.annotation.ActionLayout;
+import org.apache.causeway.applib.annotation.DomainService;
+import org.apache.causeway.applib.annotation.NatureOfService;
+import org.apache.causeway.applib.annotation.PriorityPrecedence;
+import org.apache.causeway.applib.services.factory.FactoryService;
+
+import lombok.RequiredArgsConstructor;
+
+@Named("demo.ScalarValueTypeMenu")
+@DomainService(nature=NatureOfService.VIEW)
[email protected](PriorityPrecedence.EARLY)
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+public class ScalarValueTypeMenu {
+
+    private final FactoryService factoryService;
+
+    @Action
+    @ActionLayout(cssClassFa="fa-cubes", describedAs = "Custom scalar value 
types")
+    public ScalarValueTypePage scalarValueTypes(){
+        ScalarValueTypePage page = factoryService.viewModel(new 
ScalarValueTypePage());
+        page.setEmailAddress(EmailAddress.of("[email protected]"));
+        return page;
+    }
+}
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage-description.adoc
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage-description.adoc
new file mode 100644
index 0000000000..768c054992
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage-description.adoc
@@ -0,0 +1,104 @@
+:Notice: Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements. See the NOTICE file distributed with this work 
for additional information regarding copyright ownership. The ASF licenses this 
file to you 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. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by 
applicable law or ag [...]
+
+
+"Value types" represent concepts that have intrinsic value and are immutable.
+They are often used to model concepts such as money, dates, addresses, and 
other types of measurements or quantities that have a specific value.
+
+Value types are distinct from "entity types," which represent objects with a 
unique identity that can change over time.
+In contrast, value types have no identity of their own, and two value objects 
with the same values are considered equal.
+It's also common for value types to define an algebra, where they can be 
combined through operations to produce new values.
+
+A scalar value type has a single data attribute, while a composite value type 
has multiple.
+This page demonstrates the use of *scalar* value types.
+
+
+== How this demo works
+
+This page object defines a single property whose type is a value type, 
`EmailAddress`.
+This is in essence a string, but with an additional constraint (a regex) as to 
the formatting of that string.
+
+In terms of code:
+
+* The `EmailAddress` type is defined as:
++
+[source,java]
+.EmailAddress.java
+----
+include::EmailAddress.java[tags=class]
+----
+<.> Defines this as a value type to the framework
+<.> Uses lombok to define getters, a `hashCode()`, `equals()`, `toString()` 
and a constructor.
+<.> The single data attribute
+
+* this page object defines this as a single property:
++
+[source,java]
+.ScalarValueTypePage.java
+----
+include::ScalarValueTypePage.java[tags=class]
+----
+
+=== Value Semantics Provider
+
+The framework also needs to know the structure of the value type.
+This is done by providing an implementation of a 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/value/semantics/ValueSemanticsProvider.html[ValueSemanticsProvider].
+
+* the `ValueSemanticsProvider` implementation is:
++
+[source,java,indent=0]
+.EmailAddressValueSemantics.java
+----
+include::EmailAddressValueSemantics.java[tags=class]
+----
+<.> inherits from 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/value/semantics/ValueSemanticsAbstract.html[ValueSemanticsAbstract],
 a convenience adapter.
+
+* 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 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/services/publishing/spi/CommandSubscriber.html#section-top[CommandSubscriber]
 (see 
link:https://causeway.apache.org/refguide/2.0.0-RC1/applib/index/services/command/Command.html[Command]
 and 
link:https://causeway.apache.org/refguide/2.0.0-RC1/schema/cmd.html[CommandDto]).
++
+[source,java,indent=0]
+----
+include::EmailAddressValueSemantics.java[tags=compose]
+----
+
+* 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.
++
+[source,java,indent=0]
+----
+include::EmailAddressValueSemantics.java[tags=getRenderer]
+----
+
+* the `getDefaultsProvider()` provides an initial value (eg non-nullable 
properties):
++
+[source,java,indent=0]
+----
+include::EmailAddressValueSemantics.java[tags=getDefaultsProvider]
+----
+
+* the `getParser()` is used to convert the string (entered in the UI) into the 
value type.
+If the value entered is invalid, then an exception can be thrown.
++
+[source,java,indent=0]
+----
+include::EmailAddressValueSemantics.java[tags=getParser]
+----
+
+* the `getIdStringifier()` allows the value type to be used as (part of) an 
identifier of the object.
+The string returned must be URL safe.
++
+[source,java,indent=0]
+----
+include::EmailAddressValueSemantics.java[tags=getIdStringifier]
+----
+
+Comparing scalar value types to composite value types, you'll notice that a 
scalar type doesn't need to define a "default" mixin; instead the `Parser` 
performs an equivalent role.
+
+
+=== Integrating with an ORM
+
+Be aware that if using the value type within an entity, then it will also be 
necessary to implement additional code to instruct the ORM how to convert the 
value type into the database.
+The ORMs offer several alternatives, but for example:
+
+* if using JPA, then implement `javax.persistence.AttributeConverter`
+* if using JDO, then implement 
`org.datanucleus.store.types.converters.TypeConverter`
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage.java
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage.java
new file mode 100644
index 0000000000..9b44327cf2
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *        http://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 demoapp.dom.domain.objects.progmodel.customvalues;
+
+import javax.inject.Named;
+
+import org.apache.causeway.applib.annotation.DomainObject;
+import org.apache.causeway.applib.annotation.Editing;
+import org.apache.causeway.applib.annotation.Nature;
+import org.apache.causeway.applib.annotation.ObjectSupport;
+import org.apache.causeway.applib.annotation.Property;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import demoapp.dom._infra.asciidocdesc.HasAsciiDocDescription;
+
+// tag::class[]
+@Named("demo.ScalarValueTypePage")
+@DomainObject(nature=Nature.VIEW_MODEL)
+public class ScalarValueTypePage implements HasAsciiDocDescription {
+    // ...
+// end::class[]
+    @ObjectSupport public String title() {
+        return "Scalar Value Types";
+    }
+
+
+// tag::class[]
+    @Property(editing = Editing.ENABLED)
+    @Getter @Setter
+    private EmailAddress emailAddress;
+}
+// end::class[]
diff --git 
a/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage.layout.xml
 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage.layout.xml
new file mode 100644
index 0000000000..d0a2776b2b
--- /dev/null
+++ 
b/examples/demo/domain/src/main/java/demoapp/dom/domain/objects/progmodel/customvalues/ScalarValueTypePage.layout.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+       license agreements. See the NOTICE file distributed with this work for 
additional
+       information regarding copyright ownership. The ASF licenses this file to
+       you 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 http://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. -->
+<bs3:grid
+        
xsi:schemaLocation="https://causeway.apache.org/applib/layout/component 
https://causeway.apache.org/applib/layout/component/component.xsd   
https://causeway.apache.org/applib/layout/grid/bootstrap3 
https://causeway.apache.org/applib/layout/grid/bootstrap3/bootstrap3.xsd";
+        xmlns:bs3="https://causeway.apache.org/applib/layout/grid/bootstrap3";
+        xmlns:cpt="https://causeway.apache.org/applib/layout/component";
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+    <bs3:row>
+        <bs3:col span="10" unreferencedActions="true">
+            <cpt:domainObject/>
+        </bs3:col>
+        <bs3:col span="2">
+            <cpt:fieldSet name="" id="sources" />
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="6">
+            <bs3:row>
+                <bs3:col span="12">
+                    <cpt:fieldSet name="General" id="general">
+                        <cpt:property id="emailAddress"/>
+                    </cpt:fieldSet>
+                </bs3:col>
+            </bs3:row>
+            <bs3:row>
+                <bs3:col span="12">
+                    <cpt:fieldSet name="Other" id="other" 
unreferencedProperties="true"/>
+                </bs3:col>
+            </bs3:row>
+        </bs3:col>
+        <bs3:col span="6">
+               <cpt:fieldSet name="Description" id="description">
+                <cpt:action id="clearHints" position="PANEL" />
+                <cpt:action id="rebuildMetamodel" position="PANEL"/>
+                               <cpt:action id="downloadLayout"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="inspectMetamodel"  
position="PANEL_DROPDOWN"/>
+                <cpt:action id="downloadMetamodelXml"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="downloadJdoMetamodel"  
position="PANEL_DROPDOWN"/>
+                <cpt:action id="recentCommands"  position="PANEL_DROPDOWN"/>
+                <cpt:action id="recentExecutions"  position="PANEL_DROPDOWN"/>
+                <cpt:action id="recentAuditTrailEntries"  
position="PANEL_DROPDOWN"/>
+                               <cpt:action id="impersonateWithRoles"  
position="PANEL_DROPDOWN"/>
+                <cpt:action id="openRestApi" position="PANEL_DROPDOWN" />
+                <cpt:property id="description"/>
+            </cpt:fieldSet>
+        </bs3:col>
+    </bs3:row>
+    <bs3:row>
+        <bs3:col span="12" unreferencedCollections="true"/>
+    </bs3:row>
+</bs3:grid>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml 
b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
index 1fbb3e3c0c..e7e24639ad 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/menubars.layout.xml
@@ -52,6 +52,7 @@ For latest we use: 
https://raw.githubusercontent.com/apache/causeway/master/anto
                 <mb3:named>Prog Model</mb3:named>
                 <mb3:serviceAction objectType="demo.MixinMenu" id="mixins"/>
                 <mb3:serviceAction objectType="demo.CompositeValueTypeMenu" 
id="compositeValueTypes"/>
+                <mb3:serviceAction objectType="demo.ScalarValueTypeMenu" 
id="scalarValueTypes"/>
                 <mb3:serviceAction objectType="demo.EmbeddedTypeMenuJpa" 
id="embeddedTypes"/>
                 <mb3:serviceAction objectType="demo.EmbeddedTypeMenuJdo" 
id="embeddedTypes"/>
             </mb3:section>
diff --git a/examples/demo/domain/src/main/resources/static/css/application.css 
b/examples/demo/domain/src/main/resources/static/css/application.css
index ba441dea35..ccc92ee0e0 100644
--- a/examples/demo/domain/src/main/resources/static/css/application.css
+++ b/examples/demo/domain/src/main/resources/static/css/application.css
@@ -352,7 +352,7 @@ tr.odd.custom2 > td,
 .navbar-nav.primary ul.dropdown-menu,
 .navbar-nav.secondary ul.dropdown-menu,
 .navbar-nav.tertiary ul.dropdown-menu {
-    max-height: 800px;
+    max-height: 850px;
 }
 
 div.sect1 > h2 {

Reply via email to