sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Add documentation on some portrayal classes.
Date Tue, 04 Feb 2020 19:06:57 GMT
This is an automated email from the ASF dual-hosted git repository.

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 012d9b3  Add documentation on some portrayal classes.
012d9b3 is described below

commit 012d9b33727da3f65532bffc1891a75cb048755c
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Feb 4 20:01:56 2020 +0100

    Add documentation on some portrayal classes.
---
 .../apache/sis/coverage/grid/GridCoverage2D.java   |   1 -
 .../java/org/apache/sis/internal/map/MapItem.java  | 317 ++++++++++++++++-----
 .../org/apache/sis/internal/map/Presentation.java  |   1 -
 .../apache/sis/internal/map/RenderException.java   |  40 ++-
 .../org/apache/sis/internal/map/package-info.java  |   8 +-
 5 files changed, 275 insertions(+), 92 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
index 2f8eabd..405386a 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
@@ -59,7 +59,6 @@ import static java.lang.Math.subtractExact;
 import static java.lang.Math.toIntExact;
 
 // Branch-specific imports
-
 import org.opengis.coverage.CannotEvaluateException;
 import org.opengis.coverage.PointOutsideCoverageException;
 
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java
index d34dc9b..b432dcd 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/MapItem.java
@@ -16,140 +16,229 @@
  */
 package org.apache.sis.internal.map;
 
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.HashMap;
 import java.util.Map;
+import java.util.HashMap;
+import java.util.Arrays;
 import java.util.Objects;
-import javax.swing.event.EventListenerList;
+import java.util.ConcurrentModificationException;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import org.opengis.util.InternationalString;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.ArraysExt;
 
 
 /**
  * Parent class of all map elements.
+ * This base class does not make any assumption about how this {@code MapItem} will be rendered;
+ * the actual feature or coverage data, together with styling information, are provided by
subclasses.
+ * A {@code MapItem} contains the following properties:
+ *
+ * <ul>
+ *   <li>An {@linkplain #getIdentifier() identifier}, which can be any {@link String}
at developer choice.</li>
+ *   <li>A human-readable {@linkplain #getTitle() title} for pick lists, for example
in GUI.</li>
+ *   <li>A {@linkplain #getAbstract() narrative description} providing additional information.</li>
+ * </ul>
  *
- * <p>
- * NOTE: this class is a first draft subject to modifications.
- * </p>
+ * Additional information can be added in a map of {@linkplain #getUserProperties() user
properties}.
+ *
+ * <h2>Synchronization</h2>
+ * {@code MapItem} instances are not thread-safe. Synchronization, if desired, is caller
responsibility.
  *
  * @author  Johann Sorel (Geomatys)
- * @version 2.0
- * @since   2.0
+ * @version 1.1
+ * @since   1.1
  * @module
  */
 public abstract class MapItem {
-
-    /** Identifies a change in the map item identifier. */
+    /**
+     * The {@value} property name, used for notifications about changes in map item identifier.
+     * The identifier (or name) can be used to reference the item externally.
+     * Associated values are instances of {@link String}.
+     *
+     * @see #getIdentifier()
+     * @see #setIdentifier(String)
+     * @see #addPropertyChangeListener(String, PropertyChangeListener)
+     *
+     * @todo This property seems to be named {@code "se:Name"} in SLD specification. Should
we rename?
+     */
     public static final String IDENTIFIER_PROPERTY = "identifier";
-    /** Identifies a change in the map item title. */
+
+    /**
+     * The {@value} property name, used for notifications about changes in map item title.
+     * The title is a short description for item that might be displayed in a GUI pick list.
+     * Associated values are instances of {@link String} or {@link InternationalString}.
+     *
+     * @see #getTitle()
+     * @see #setTitle(CharSequence)
+     * @see #addPropertyChangeListener(String, PropertyChangeListener)
+     */
     public static final String TITLE_PROPERTY = "title";
-    /** Identifies a change in the map item abstract description. */
+
+    /**
+     * The {@value} property name, used for notifications about changes in map item description.
+     * The abstract is a narrative description providing additional information.
+     * It is more detailed than the {@value #TITLE_PROPERTY} property and may be a few paragraphs
long.
+     * Associated values are instances of {@link String} or {@link InternationalString}.
+     *
+     * @see #getAbstract()
+     * @see #setAbstract(CharSequence)
+     * @see #addPropertyChangeListener(String, PropertyChangeListener)
+     */
     public static final String ABSTRACT_PROPERTY = "abstract";
-    /** Identifies a change in the map item visibility state. */
-    public static final String VISIBLE_PROPERTY = "visible";
 
-    private final EventListenerList listeners = new EventListenerList();
+    /**
+     * The {@value} property name, used for notifications about changes in map item visibility
state.
+     * Associated values are instances of {@link Boolean}.
+     */
+    public static final String VISIBLE_PROPERTY = "visible";
 
     /**
      * Identifier of this map item.
+     *
+     * @see #IDENTIFIER_PROPERTY
+     * @see #getIdentifier()
      */
     private String identifier;
+
     /**
      * The title of this map item, for display to the user.
+     *
+     * @see #TITLE_PROPERTY
+     * @see #getTitle()
      */
     private CharSequence title;
 
     /**
-     * A description of this map item, for display to the user.
+     * A description of this map item, for display to the user. The property name is
+     * {@value #ABSTRACT_PROPERTY} but we use a different field name because "abstract"
+     * is a reserved keyword.
+     *
+     * @see #ABSTRACT_PROPERTY
+     * @see #getAbstract()
      */
-    private CharSequence abtract;
+    private CharSequence description;
 
     /**
      * Whether this item should be shown on the map.
+     *
+     * @see #VISIBLE_PROPERTY
+     * @see #isVisible()
      */
-    private boolean visible = true;
+    private boolean visible;
 
     /**
-     * Additional user defined properties.
+     * Additional user defined properties, created when first requested.
+     *
+     * @see #getUserProperties()
      */
     private Map<String,Object> userMap;
 
     /**
+     * The registered listeners for each property, created when first needed.
+     *
+     * @see #addPropertyChangeListener(String, PropertyChangeListener)
+     * @see #removePropertyChangeListener(String, PropertyChangeListener)
+     */
+    private Map<String,PropertyChangeListener[]> listeners;
+
+    /**
      * Only used by classes in this package.
      */
     MapItem() {
+        visible = true;
     }
 
     /**
-     * Returns the identifier of this map item.
+     * Returns the identifier of this map item. The identifier can be any character string
at developer choice;
+     * there is currently no restriction on the identifier form and no restriction about
identifier uniqueness.
+     * The identifier is currently not used by Apache SIS; it is made available as a user
convenience for
+     * referencing {@code MapItem} instances externally.
+     *
+     * <p>NOTE: restriction about identifier form and uniqueness may be added in a
future version.</p>
      *
      * @return identifier, or {@code null} if none.
+     *
+     * @see #IDENTIFIER_PROPERTY
      */
     public String getIdentifier() {
         return identifier;
     }
 
     /**
-     * Sets a new identifier for this map item.
+     * Sets a new identifier for this map item. If this method is never invoked, the default
value is {@code null}.
+     * If the given value is different than the previous value, then a change event is sent
to all listeners
+     * registered for the {@value #IDENTIFIER_PROPERTY} property.
      *
-     * @param  identifier  identifier, or {@code null} if none.
+     * @param  newValue  the new identifier, or {@code null} if none.
      */
-    public void setIdentifier(String identifier) {
-        if (!Objects.equals(this.identifier, identifier)) {
-            CharSequence old = this.identifier;
-            this.identifier = identifier;
-            firePropertyChange(IDENTIFIER_PROPERTY, old, identifier);
+    public void setIdentifier(final String newValue) {
+        final String oldValue = identifier;
+        if (!Objects.equals(oldValue, newValue)) {
+            identifier = newValue;
+            firePropertyChange(IDENTIFIER_PROPERTY, oldValue, newValue);
         }
     }
 
     /**
-     * Returns the title of this map item.
-     * This title should be user friendly and may be an {@link org.opengis.util.InternationalString}.
+     * Returns a human-readable short description for pick lists.
+     * This title should be user friendly and may be a {@link String} or {@link InternationalString}
instance.
      * It shall not be used as an identifier.
      *
-     * @return title to be shown to the user, or {@code null} if none.
+     * @return a short description to be shown to the user, or {@code null} if none.
+     *
+     * @see #TITLE_PROPERTY
      */
     public CharSequence getTitle() {
         return title;
     }
 
     /**
-     * Sets a new title for this map item.
+     * Sets a new human-readable short description for pick lists. If this method is never
invoked,
+     * the default value is {@code null}. If the given value is different than the previous
value,
+     * then a change event is sent to all listeners registered for the {@value #TITLE_PROPERTY}
property.
      *
-     * @param  title  title to be shown to the user, or {@code null} if none.
+     * @param  newValue  a short description to be shown to the user, or {@code null} if
none.
      */
-    public void setTitle(CharSequence title) {
-        if (!Objects.equals(this.title, title)) {
-            CharSequence old = this.title;
-            this.title = title;
-            firePropertyChange(TITLE_PROPERTY, old, title);
+    public void setTitle(final CharSequence newValue) {
+        final CharSequence oldValue = title;
+        if (!Objects.equals(oldValue, newValue)) {
+            title = newValue;
+            firePropertyChange(TITLE_PROPERTY, oldValue, newValue);
         }
     }
 
     /**
-     * Returns the description of this map item.
-     * This description should be user friendly and may be an {@link org.opengis.util.InternationalString}.
+     * Returns a narrative description providing additional information.
+     * The abstract is more detailed than the {@linkplain #getTitle() title} property and
may be a few paragraphs long.
+     * This abstract should be user friendly and may be a {@link String} or {@link InternationalString}
instance.
+     *
+     * @return narrative description to be shown to the user, or {@code null} if none.
      *
-     * @return description to be shown to the user, or {@code null} if none.
+     * @see #ABSTRACT_PROPERTY
      */
     public CharSequence getAbstract() {
-        return abtract;
+        return description;
     }
 
     /**
-     * Sets a new description for this map item.
+     * Sets a new a narrative description providing additional information. If this method
is never invoked,
+     * the default value is {@code null}. If the given value is different than the previous
value, then
+     * a change event is sent to all listeners registered for the {@value #ABSTRACT_PROPERTY}
property.
      *
-     * @param  abtract  title to be shown to the user, or {@code null} if none.
+     * @param  newValue  a narrative description to be shown to the user, or {@code null}
if none.
      */
-    public void setAbstract(CharSequence abtract) {
-        if (!Objects.equals(this.abtract, abtract)) {
-            CharSequence old = this.abtract;
-            this.abtract = abtract;
-            firePropertyChange(ABSTRACT_PROPERTY, old, abtract);
+    public void setAbstract(final CharSequence newValue) {
+        final CharSequence oldValue = description;
+        if (!Objects.equals(oldValue, newValue)) {
+            description = newValue;
+            firePropertyChange(ABSTRACT_PROPERTY, oldValue, newValue);
         }
     }
 
     /**
-     * Return whether this item should be shown on the map.
+     * Returns whether this item should be shown on the map. If this item is a {@code MapGroup},
+     * then a {@code false} visibility status implies that all group components are also
hidden.
      *
      * @return {@code true} if this item is visible.
      */
@@ -159,52 +248,134 @@ public abstract class MapItem {
 
     /**
      * Sets whether this item should be shown on the map.
-     * If this item is a {@code MapGroup}, then hiding this group should hide all components
in this group.
+     * If this method is never invoked, the default value is {@code true}.
+     * If the given value is different than the previous value, then a change event
+     * is sent to all listeners registered for the {@value #VISIBLE_PROPERTY} property.
      *
-     * @param visible {@code false} to hide this item and all it's components.
+     * <p>If this item is a {@code MapGroup}, then hiding this group should hide all
components in this group,
+     * but without changing the individual {@value #VISIBLE_PROPERTY} property of those components.
+     * Consequently making the group visible again restore each component to the visibility
state
+     * it has before the group was hidden (assuming those states have not been changed in
other ways).</p>
+     *
+     * @param  newValue  {@code false} to hide this item and all it's components.
      */
-    public void setVisible(boolean visible) {
-        if (this.visible != visible) {
-            this.visible = visible;
-            firePropertyChange(VISIBLE_PROPERTY, !visible, visible);
+    public void setVisible(final boolean newValue) {
+        final boolean oldValue = visible;
+        if (oldValue != newValue) {
+            visible = newValue;
+            firePropertyChange(VISIBLE_PROPERTY, oldValue, newValue);
         }
     }
 
     /**
-     * @return map of all user properties.
-     *          This is the live map.
+     * Returns a modifiable map of user properties.
+     * The content of this map is left to users; Apache SIS does not use it in any way.
+     * This map is not thread-safe; synchronization if desired is user responsibility.
+     *
+     * @return map of user properties. This map is live: changes in this map
+     *         are immediately reflected in this {@code MapItem}.
      */
-    public synchronized Map<String,Object> getUserProperties() {
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    public Map<String,Object> getUserProperties() {
         if (userMap == null) {
             userMap = new HashMap<>();
         }
         return userMap;
     }
+
     /**
-     * Register a property listener.
+     * Register a listener for the property of the given name.
+     * The listener will be notified every time that the property of the given name got a
new value.
+     * The {@code propertyName} can be one of the following values:
+     *
+     * <ul>
+     *   <li>{@value #IDENTIFIER_PROPERTY} — for changes in identifier of this map
item.</li>
+     *   <li>{@value #TITLE_PROPERTY}      — for changes in human-readable short
description.</li>
+     *   <li>{@value #ABSTRACT_PROPERTY}   — for changes in narrative description.</li>
+     *   <li>{@value #VISIBLE_PROPERTY}    — for changes in visibility state.</li>
+     *   <li>Any other property defined by subclasses.</li>
+     * </ul>
      *
-     * @param listener property listener to register
+     * If the same listener is registered twice for the same property, then it will be notified
twice
+     * (this method does not perform duplication checks).
+     *
+     * @param  propertyName  name of the property to listen.
+     * @param  listener      property listener to register.
      */
-    public final void addPropertyChangeListener(PropertyChangeListener listener) {
-        listeners.add(PropertyChangeListener.class, listener);
+    public final void addPropertyChangeListener(final String propertyName, final PropertyChangeListener
listener) {
+        ArgumentChecks.ensureNonEmpty("propertyName", propertyName);
+        ArgumentChecks.ensureNonNull("listener", listener);
+        if (listeners == null) {
+            listeners = new HashMap<>(4);       // Assume few properties will be listened.
+        }
+        final PropertyChangeListener[] oldList = listeners.get(propertyName);
+        final PropertyChangeListener[] newList;
+        final int n;
+        if (oldList != null) {
+            n = oldList.length;
+            newList = Arrays.copyOf(oldList, n+1);
+        } else {
+            n = 0;
+            newList = new PropertyChangeListener[1];
+        }
+        newList[n] = listener;
+        if (!listeners.replace(propertyName, oldList, newList)) {
+            // Opportunistic safety against some multi-threading misuse.
+            throw new ConcurrentModificationException();
+        }
     }
 
     /**
-     * Unregister a property listener.
+     * Unregister a property listener. The given {@code propertyName} can be any of the name
documented in
+     * {@link #addPropertyChangeListener(String, PropertyChangeListener)}. If the specified
listener is not
+     * registered for the specified property, then nothing happen. If the listener has been
registered twice,
+     * then only one registration is removed (one registration will remain).
      *
-     * @param listener property listener to register
+     * @param  propertyName  name of the listened property.
+     * @param  listener      property listener to unregister.
      */
-    public final void removePropertyChangeListener(PropertyChangeListener listener) {
-        listeners.remove(PropertyChangeListener.class, listener);
+    public final void removePropertyChangeListener(final String propertyName, final PropertyChangeListener
listener) {
+        ArgumentChecks.ensureNonEmpty("propertyName", propertyName);
+        ArgumentChecks.ensureNonNull("listener", listener);
+        if (listeners != null) {
+            final PropertyChangeListener[] oldList = listeners.get(propertyName);
+            if (oldList != null) {
+                for (int i=oldList.length; --i >= 0;) {
+                    if (oldList[i] == listener) {
+                        if (oldList.length != 1) {
+                            final PropertyChangeListener[] newList = ArraysExt.remove(oldList,
i, 1);
+                            if (listeners.replace(propertyName, oldList, newList)) {
+                                return;
+                            }
+                        } else if (listeners.remove(propertyName, oldList)) {
+                            return;
+                        }
+                        // Opportunistic safety against some multi-threading misuse.
+                        throw new ConcurrentModificationException();
+                    }
+                }
+            }
+        }
     }
 
+    /**
+     * Notifies all registered listener that a property of the given name changed its value.
+     * It is caller responsibility to verify that the old and new values are not equal
+     * (this method does not verify).
+     *
+     * @param  propertyName  name of the property that changed its value.
+     * @param  oldValue      the old property value (may be {@code null}).
+     * @param  newValue      the new property value (may be {@code null}).
+     */
     protected void firePropertyChange(final String propertyName, final Object oldValue, final
Object newValue) {
-        final PropertyChangeListener[] listPs = listeners.getListeners(PropertyChangeListener.class);
-        if (listPs.length == 0) return;
-
-        final PropertyChangeEvent event = new PropertyChangeEvent(this,propertyName,oldValue,newValue);
-        for (PropertyChangeListener listener : listPs) {
-            listener.propertyChange(event);
+        if (listeners != null) {
+            final PropertyChangeListener[] list = listeners.get(propertyName);
+            if (list != null) {
+                final PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName,
oldValue, newValue);
+                for (final PropertyChangeListener listener : list) {
+                    listener.propertyChange(event);
+                }
+            }
         }
     }
 }
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Presentation.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Presentation.java
index 0673be1..065c7ed 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Presentation.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/Presentation.java
@@ -89,5 +89,4 @@ public abstract class Presentation {
     public void setCandidate(Feature feature) {
         this.candidate = feature;
     }
-
 }
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/RenderException.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/RenderException.java
index 36b3edd..ffab9dd 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/RenderException.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/RenderException.java
@@ -16,34 +16,46 @@
  */
 package org.apache.sis.internal.map;
 
-import org.apache.sis.util.ArgumentChecks;
-
 
 /**
  * Thrown when a map rendering process failed.
  *
- * <p>
- * NOTE: this class is a first draft subject to modifications.
- * </p>
- *
  * @author  Johann Sorel (Geomatys)
- * @version 2.0
- * @since   2.0
+ * @version 1.1
+ * @since   1.1
  * @module
  */
 public class RenderException extends Exception {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 4185833217030999642L;
 
+    /**
+     * Creates an exception with the specified details message.
+     *
+     * @param message  the detail message.
+     */
     public RenderException(final String message) {
         super(message);
-        ArgumentChecks.ensureNonEmpty("message", message);
     }
 
-    public RenderException(final Throwable throwable) {
-        this(((throwable.getMessage() == null) ? "No message" : throwable.getMessage()),
throwable);
+    /**
+     * Creates an exception with the specified cause and no details message.
+     *
+     * @param cause  the cause for this exception.
+     */
+    public RenderException(final Throwable cause) {
+        super(cause);
     }
 
-    public RenderException(final String message, final Throwable throwable) {
-        super(message, throwable);
-        ArgumentChecks.ensureNonEmpty("message", message);
+    /**
+     * Creates an exception with the specified details message and cause.
+     *
+     * @param message  the detail message in the default locale.
+     * @param cause    the cause for this exception.
+     */
+    public RenderException(final String message, final Throwable cause) {
+        super(message, cause);
     }
 }
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/package-info.java
b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/package-info.java
index 4fcaf00..324ac5a 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/package-info.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/package-info.java
@@ -24,11 +24,13 @@
  * Some classes in this package will move to public API after we gained enough confidence
  * about their stability.</p>
  *
- * @todo Since everything is about maps in this package, should we omit the {@code Map} prefix
in class names?
+ * <h2>Synchronization</h2>
+ * Unless otherwise specified, classes in this package are not thread safe.
+ * Synchronization, if desired, must be done by the caller.
  *
  * @author  Johann Sorel (Geomatys)
- * @version 2.0
- * @since   2.0
+ * @version 1.1
+ * @since   1.1
  * @module
  */
 package org.apache.sis.internal.map;


Mime
View raw message