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

desruisseaux pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sis-site.git

commit 6c4b10acff054f46d21caf4bd7e115061664aa82
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Nov 21 14:25:06 2023 +0100

    Better documentation about the handling of NilReason and the view as 
TreeTableMap.
---
 .../developer-guide/introduction/Conventions.html  |   4 +-
 .../developer-guide/introduction/DataAccess.html   |   3 +-
 .../developer-guide/metadata/NavigateMetadata.html | 168 ++++++++++++++++++---
 content/developer-guide/metadata/NilReason.html    |  60 +++++++-
 content/developer-guide/metadata/index.html        |   3 +-
 static/book/book.css                               |  14 ++
 6 files changed, 224 insertions(+), 28 deletions(-)

diff --git a/content/developer-guide/introduction/Conventions.html 
b/content/developer-guide/introduction/Conventions.html
index 9202e257..420016e8 100644
--- a/content/developer-guide/introduction/Conventions.html
+++ b/content/developer-guide/introduction/Conventions.html
@@ -33,7 +33,7 @@
     -->
     <section>
       <header>
-        <h2 id="TypographicConventions">Typographic conventions</h2>
+        <h2 id="TypographicConventions">Typographic conventions in this 
document</h2>
       </header>
       <p>
         The elements defined in a computer language, such as classes and 
methods in Java or elements in an <abbr>XML</abbr> document,
@@ -66,7 +66,7 @@
     </section>
     <section>
       <header>
-        <h2 id="CodingConventions">Coding conventions</h2>
+        <h2 id="CodingConventions">Coding conventions in Apache 
<abbr>SIS</abbr></h2>
       </header>
       <p>
         Apache SIS implements most GeoAPI interfaces by a classes of the same 
name than the interface
diff --git a/content/developer-guide/introduction/DataAccess.html 
b/content/developer-guide/introduction/DataAccess.html
index f7039a12..fc9ce044 100644
--- a/content/developer-guide/introduction/DataAccess.html
+++ b/content/developer-guide/introduction/DataAccess.html
@@ -70,8 +70,7 @@
         For example, the following code read a metadata file from a Landsat-8 
image and prints the declared geographic bounding box:
       </p>
 
-<pre><code>
-import org.opengis.metadata.Metadata;
+<pre><code>import org.opengis.metadata.Metadata;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStores;
diff --git a/content/developer-guide/metadata/NavigateMetadata.html 
b/content/developer-guide/metadata/NavigateMetadata.html
index a8883b06..578ccdfe 100644
--- a/content/developer-guide/metadata/NavigateMetadata.html
+++ b/content/developer-guide/metadata/NavigateMetadata.html
@@ -44,7 +44,7 @@
       </p>
       <ul>
         <li>As key-value pairs in a <code>Map</code> (from 
<code>java.util</code>).</li>
-        <li>As a <code>TreeTable</code> (from 
<code>org.apache.sis.util.collection</code>).</li>
+        <li>As a <code class="SIS">TreeTable</code> (from 
<code>org.apache.sis.util.collection</code>).</li>
         <li>As a table record in a database (using 
<code>org.apache.sis.metadata.sql</code>).</li>
         <li>As an <abbr>XML</abbr> document conforms to <abbr>ISO</abbr> 
standard schema.</li>
       </ul>
@@ -56,13 +56,22 @@
       <h3 id="GetMetadataElement">Direct access via getter methods</h3>
       <p>
         All metadata classes provide getter, and sometime setter, methods for 
their properties.
-        Some properties accept many values, in which case the property type is 
a collection.
-        The following example prints the range of latitudes of all data 
descriptions
-        found in a given root <code>Metadata</code> object:
+        The following example prints all ranges of latitudes found in a 
metadata.
+        It may be, for example, a metadata describing the extent of a raster 
file.
+        Fetching that information requires navigating through the following 
steps:
+      </p><p style="text-align:center">
+        <code class="API">Metadata</code>
+        ⟶  <code class="OGC">identificationInfo</code>
+        ⟶  <code class="OGC">extent</code>
+        ⟶  <code class="OGC">geographicElement</code>
+        ⟶ (<code class="OGC">southBoundLatitude</code>, <code 
class="OGC">northBoundLatitude</code>)
+      </p><p>
+        Some properties accept many values, in which case the code must 
iterate over the elements of a collection.
+        Furthermore, some properties are available only in a sub-interface, in 
which case <code>instanceof</code>
+        checks are required. The resulting code is as below:
       </p>
 
-<pre><code>
-import org.opengis.metadata.metadata;
+<pre><code>import org.opengis.metadata.metadata;
 import org.opengis.metadata.extent.Extent;
 import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.metadata.extent.GeographicBoundingBox;
@@ -95,14 +104,16 @@ void main() {
         For example the following method navigates through different branches 
where North, South, East and West data bounds may be found:
       </p>
 
-<pre><code>
-import org.opengis.metadata.metadata;
+<pre><code>import org.opengis.metadata.metadata;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.apache.sis.metadata.iso.extent.Extents;
 
 void main() {
     Metadata metadata = ...;    // For example, metadata read from a data 
store.
     GeographicBoundingBox bbox = Extents.getGeographicBoundingBox(extent);
+    if (box != null) {
+        // Same as in the previous example.
+    }
 }</code></pre>
 
       <p>
@@ -111,6 +122,88 @@ void main() {
         <code>Citations</code>, <code>Envelopes</code>, <code>Matrices</code> 
and <code>MathTransforms</code>.
       </p>
 
+
+
+
+      <h3 id="SetMetadataElement">Direct construction via setter methods</h3>
+      <p>
+        For every getter method, there is a corresponding setter method.
+        However the setter methods are not defined in the GeoAPI interfaces, 
which are read-only.
+        For invoking setter methods, it is necessary to use the Apache 
<abbr>SIS</abbr> implementation classes directly.
+        It is straightforward when the metadata objects are created and 
populated in the same code, as there is nothing special to do.
+        The following example creates a citation using three different ways to 
specify a value:
+        at construction time using convenience constructors,
+        using setter methods,
+        and by adding elements directly in the collections, which are 
modifiable.
+      </p>
+<pre><code>import org.apache.sis.metadata.iso.DefaultIdentifier;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.opengis.metadata.citation.PresentationForm;          // A code list.
+
+void main() {
+    // Following constructor is a convenience for setting the `title` property 
at construction time.
+    var citation = new DefaultCitation("Climate Change 2022: Impacts, 
Adaptation, and Vulnerability");
+    citation.getPresentationForms().add(PresentationForm.DOCUMENT_DIGITAL);
+
+    var identifier = new DefaultIdentifier();
+    identifier.setCode("10.1017/9781009325844");
+    identifier.setCodeSpace("DOI");
+    citation.getIdentifiers().add(identifier);
+
+    // Do not allow the citation to be modified after this point (optional).
+    citation.transitionTo(DefaultCitation.State.FINAL);
+    System.out.println(citation);
+}</code></pre>
+
+      <p>
+        Output is as below:
+      </p>
+
+<pre><samp>Citation……………………………………… Climate Change 2022: Impacts, Adaptation, 
and Vulnerability
+  ├─Identifier……………………… 10.1017/9781009325844
+  │   └─Code space…………… DOI
+  └─Presentation form…… Document digital</samp></pre>
+
+      <p>
+        Often, the newly created metadata will be reused many times.
+        It may be for example a static final constant, or a cached value.
+        In such case, it may be desirable to declare the metadata as 
unmodifiable after its construction.
+        This is the purpose of the call to <code 
class="SIS">transitionTo(State.FINAL)</code> in above example.
+        After that call, any attempt to modify the metadata will cause an 
<code class="SIS">UnmodifiableMetadataException</code> to be thrown.
+      </p><p>
+        Setting the values of a metadata object may be more tricky when the 
implementation class is unknown.
+        Since there is no setter methods in the GeoAPI interfaces, the 
metadata may need to be converted to
+        an Apache <abbr>SIS</abbr> implementation class first, and that 
instance needs to be modifiable.
+        This is a two steps process.
+        The first step can be done by invoking the static <code 
class="SIS">castOrCopy(…)</code> method
+        which is defined in every Apache <abbr>SIS</abbr> implementation 
classes.
+        For example there is
+        a <code class="SIS">DefaultCitation.castOrCopy(<code 
class="OGC">Citation</code>)</code> method,
+        a <code class="SIS">DefaultExtent.castOrCopy(<code 
class="OGC">Extent</code>)</code> method, <i>etc.</i>
+        Those methods will either return the given instance directly when 
possible,
+        or otherwise create a <em>shallow</em> copy of that instance (i.e., 
without recursive copy of children).
+        The second step can be done in different ways. An easy way (while not 
the most efficient)
+        is to make an unconditional <em>deep</em> copy of the metadata,
+        in case that metadata was unmodifiable (<code 
class="SIS">State.FINAL</code>),
+        and also (if desired) for avoiding to change the original metadata.
+        Example:
+      </p>
+
+<pre><code>import org.opengis.metadata.citation.Citation;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.util.SimpleInternationalString;
+
+void main() {
+    Citation original = ...;    // Some pre-existing citation.
+
+    // Get a modifiable copy and change its title.
+    DefaultCitation edit = DefaultCitation.castOrCopy(original);
+    edit = (DefaultCitation) copy.deepCopy(DefaultCitation.State.EDITABLE);
+    edit.setTitle(new SimpleInternationalString("A new title"));
+}</code></pre>
+
+
+
       <h3 id="MetadataAsMap">View as key-value pairs</h3>
       <p>
         Above static methods explore fragments of metadata tree in search for 
requested information,
@@ -119,13 +212,13 @@ void main() {
         In such cases, one can view the metadata as a 
<code>java.util.Map</code> like below:
       </p>
 
-<pre><code>
-import java.util.Map;
+<pre><code>import java.util.Map;
 import org.apache.sis.metadata.MetadataStandard;
 import org.apache.sis.metadata.KeyNamePolicy;
 import org.apache.sis.metadata.ValueExistencePolicy;
 
 void main() {
+    Object metadata = ...;
     Map&lt;String,Object&gt; elements = MetadataStandard.ISO_19115.asValueMap(
             metadata,                           // Any instance from the 
org.opengis.metadata package or a sub-package.
             null,                               // Used for resolving 
ambiguities. We can ignore for this example.
@@ -154,6 +247,9 @@ void main() {
         package javadoc.
       </p>
 
+
+
+
       <h3 id="MetadataAsTreeTable">View as tree table</h3>
       <p>
         A richer alternative to the view as a map is the view as a tree table.
@@ -162,19 +258,53 @@ void main() {
       </p>
       <table>
         <caption>Columns of a tree table view of a metadata</caption>
-        <tr><th>Column</th> <th>Description</th></tr>
-        <tr><td><code class="SIS">IDENTIFIER</code></td> <td>The UML 
identifier if any, or otherwise the Java Beans name, of the metadata 
property.</td></tr>
-        <tr><td><code class="SIS">INDEX</code></td>      <td>If the property 
is a collection, then the zero-based index of the element in that 
collection.</td></tr>
-        <tr><td><code class="SIS">NAME</code></td>       <td>A human-readable 
name for the node, derived from above identifier and index.</td></tr>
-        <tr><td><code class="SIS">TYPE</code></td>       <td>The base type of 
the value (usually a GeoAPI interface).</td></tr>
-        <tr><td><code class="SIS">VALUE</code></td>      <td>The metadata 
value for the node. This column may be writable.</td></tr>
-        <tr><td><code class="SIS">NIL_REASON</code></td> <td>If the value is 
mandatory and nevertheless absent, the reason why.</td></tr>
-        <tr><td><code class="SIS">REMARKS</code></td>    <td>Remarks or 
warning on the property value.</td></tr>
+        <tr><th>Column</th> <th>Description</th> <th>Writable</th></tr>
+        <tr><td><code class="SIS">IDENTIFIER</code></td> <td>The UML 
identifier if any, or otherwise the Java Beans name, of the metadata 
property.</td><td></td></tr>
+        <tr><td><code class="SIS">INDEX</code></td>      <td>If the property 
is a collection, then the zero-based index of the element in that 
collection.</td><td></td></tr>
+        <tr><td><code class="SIS">NAME</code></td>       <td>A human-readable 
name for the node, derived from above identifier and index.</td><td></td></tr>
+        <tr><td><code class="SIS">TYPE</code></td>       <td>The base type of 
the value (usually a GeoAPI interface).</td><td></td></tr>
+<!-- SIS 1.5
+        <tr><td><code class="SIS">OBLIGATION</code></td> <td>Whether the 
property is mandatory, optional or conditional.</td><td></td></tr>
+-->
+        <tr><td><code class="SIS">VALUE</code></td>      <td>The metadata 
value for the node. This column may be writable.</td><td>✓</td></tr>
+<!-- SIS 1.5
+        <tr><td><code class="SIS">NIL_REASON</code></td> <td>If the value is 
mandatory and nevertheless absent, the reason why.</td><td>✓</td></tr>
+-->
+        <tr><td><code class="SIS">REMARKS</code></td>    <td>Remarks or 
warning on the property value.</td><td></td></tr>
       </table>
       <p>
         Tree table views are obtained in a way similar to <a 
href="#MetadataAsMap">map views</a>,
-        but using the <code class="SIS">asTreeTable(Object)</code> method 
instead of <code class="SIS">asValueMap(Object)</code>.
+        but using the <code class="SIS">asTreeTable(…)</code> method instead 
of <code class="SIS">asValueMap(…)</code>.
+        Values can be obtained for each column, and can be set for the <code 
class="SIS">VALUE</code> column
+        if the underlying metadata object is modifiable.
+        The following example prints the values of the <code 
class="SIS">NAME</code> and <code class="SIS">TYPE</code>
+        columns for a given metadata and all its children:
       </p>
+
+<pre><code>import org.apache.sis.metadata.MetadataStandard;
+import org.apache.sis.metadata.ValueExistencePolicy;
+import org.apache.sis.util.collection.TreeTable;
+import org.apache.sis.util.collection.TableColumn;
+
+class Explorer {
+    void main() {
+        Object metadata = ...;
+        TreeTable table = MetadataStandard.ISO_19115.asTreeTable(
+                metadata,                           // Any instance from the 
org.opengis.metadata package or a sub-package.
+                null,                               // Used for resolving 
ambiguities. We can ignore for this example.
+                ValueExistencePolicy.NON_EMPTY);    // Entries with null or 
empty values will be omitted.
+
+        // Print the names and types of all metadata elements having a value.
+        printRecursively(table.getRoot());
+    }
+
+    private static void printRecursively(TreeTable.Node node) {
+        String   name = node.getValue(TableColumn.NAME);
+        Class&lt;?&gt; type = node.getValue(TableColumn.TYPE);
+        System.out.println(name + " : " + type.getSimpleName());
+        node.getChildren().forEach(Explorer::printRecursively);
+    }
+}</code></pre>
     </section>
   </body>
 </html>
diff --git a/content/developer-guide/metadata/NilReason.html 
b/content/developer-guide/metadata/NilReason.html
index 274da300..e910a6f7 100644
--- a/content/developer-guide/metadata/NilReason.html
+++ b/content/developer-guide/metadata/NilReason.html
@@ -53,16 +53,70 @@
         <tr><td><code class="OGC">missing</code></td>      <td>The value 
cannot exist.</td></tr>
         <tr><td><code class="OGC">withheld</code></td>     <td>The value 
cannot be revealed.</td></tr>
         <tr><td><code class="OGC">template</code></td>     <td>The value will 
be available later.</td></tr>
+        <tr><td><code class="OGC">other</code></td>        <td>None of the 
above. Can be completed by user-suplied strings.</td></tr>
       </table>
       <p>
         The transmission of this information requires the use of a non-null 
object, even when the value is missing.
-        In such case, <abbr>SIS</abbr> will return an object that, besides 
implementing the desired GeoAPI interface,
-        also implements the <code>org.apache.sis.xml.NilObject</code> 
interface.
+        In such case, Apache <abbr>SIS</abbr> will return an object that, 
besides implementing the desired GeoAPI interface,
+        also implements the <code class="SIS">NilObject</code> interface.
         This interface flags the instances where all methods return an empty 
collection, an empty table, <code>null</code>,
         <code>NaN</code>, <code>0</code> or <code>false</code>, in this 
preference order, as permitted by the return types of the methods.
-        Each instance that implements <code>NilObject</code> provides a <code 
class="SIS">getNilReason()</code> method
+        Each instance that implements <code class="SIS">NilObject</code> 
provides a <code class="SIS">getNilReason()</code> method
         indicating why the object is nil.
+        For example, the following code specifies that a citation title is 
missing
+        because this metadata is only a template to be completed by the user:
       </p>
+
+<pre><code>import org.opengis.util.InternationalString;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
+import org.apache.sis.xml.NilReason;
+
+void main() {
+    InternationalString nil = 
NilReason.TEMPLATE.createNilObject(InternationalString.class);
+    var citation = new DefaultCitation(nil);
+
+    // Verify the reason that we have just set.
+    System.out.println("Title: " + citation.getTitle());
+    NilReason reason = NilReason.forObject(citation.getTitle());
+    System.out.println("Reason why the title is missing: " + reason);
+}</code></pre>
+
+      <p>
+        Output is as below (note that the title is empty, not null):
+      </p>
+
+<pre><samp>Title:
+Reason why the title is missing: template</samp></pre>
+
+<!-- SIS 1.5
+      <p>
+        Alternatively, if the metadata instance is known to be a subclass of 
<code class="SIS">AbstractMetadata</code>,
+        then the following substitutions can be done in the above example.
+        The advantage is that the <code class="SIS">nilReasons()</code> map 
works for a few more types
+        than what <code class="SIS">NilReason.createNilObject(Class)</code> 
can support, for example <code>Boolean</code>.
+        The inconvenient is that there is less compile-time checks, because 
properties are identified by character strings.
+      </p>
+      <div class="table">
+        <div class="row">
+          <div class="cell"><code><code 
class="SIS">NilReason.forObject</code>(citation.<code 
class="API">getTitle()</code>)</code></div>
+          <div class="cell">⟶</div>
+          <div class="cell"><code>citation.<code 
class="SIS">nilReasons()</code>.get(<code 
class="OGC">"title"</code>)</code></div>
+        </div>
+        <div class="row">
+          <div class="cell"><code>citation.<code 
class="SIS">setTitle</code>(nilReason.<code 
class="SIS">createNilObject</code>(<code 
class="OGC">InternationalString</code>.class))</code></div>
+          <div class="cell">⟶</div>
+          <div class="cell"><code>citation.<code 
class="SIS">nilReasons()</code>.put(<code class="OGC">"title"</code>, 
nilReason)</code></div>
+        </div>
+      </div>
+      <p>
+        The <code class="SIS">nilReasons()</code> is also useful for checking 
if there is any mandatory property without value.
+        That map will alway contain an entry for such cases, even if the 
associated nil reason is <code>null</code>.
+        By contrast, optional properties are contained in the map only if they 
are associated to a non-null nil reason.
+        Consequently, <code>metadata.<code 
class="SIS">nilReasons()</code>.contains(null)</code> is true if the metadata
+        contains at least one mandatory property with no value and no nil 
reason.
+      </p>
+-->
+
     </section>
   </body>
 </html>
diff --git a/content/developer-guide/metadata/index.html 
b/content/developer-guide/metadata/index.html
index a6e811b0..16d559d5 100644
--- a/content/developer-guide/metadata/index.html
+++ b/content/developer-guide/metadata/index.html
@@ -103,8 +103,7 @@
         A metadata may be created programmatically like below:
       </p>
 
-<pre><code>
-import org.apache.sis.metadata.iso.citation.DefaultCitation;
+<pre><code>import org.apache.sis.metadata.iso.citation.DefaultCitation;
 import org.opengis.metadata.citation.PresentationForm;
 
 void main() {
diff --git a/static/book/book.css b/static/book/book.css
index 09751e9c..4b9f6e45 100644
--- a/static/book/book.css
+++ b/static/book/book.css
@@ -281,6 +281,20 @@ div.row-of-boxes > div {
   padding: 6px 30px;
 }
 
+div.table {
+  display: table;
+}
+
+div.row {
+  display: table-row;
+}
+
+div.cell {
+  display: table-cell;
+  padding-left:  21px;
+  padding-right: 21px;
+}
+
 pre {
   margin-top:    21px;
   margin-bottom: 21px;

Reply via email to