sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1453026 - in /sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata: ./ iso/ iso/citation/
Date Tue, 05 Mar 2013 21:48:26 GMT
Author: desruisseaux
Date: Tue Mar  5 21:48:25 2013
New Revision: 1453026

URL: http://svn.apache.org/r1453026
Log:
Ported the metadata view as a java.util.Map, together with isEmpty() and prune() methods.

Added:
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java   (with props)
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java   (with props)
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java   (with props)
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java   (with props)
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java   (with props)
Modified:
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/TypeValuePolicy.java
    sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java?rev=1453026&r1=1453025&r2=1453026&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/AbstractMetadata.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -16,34 +16,52 @@
  */
 package org.apache.sis.metadata;
 
+import java.util.Map;
 import java.util.logging.Logger;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.LenientComparable;
+import org.apache.sis.util.collection.TreeTable;
 import org.apache.sis.util.logging.Logging;
 
+import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+
 
 /**
- * Base class for metadata implementations, providing basic operations using Java reflection.
- * All {@code AbstractMetadata} instances shall be associated to a {@link MetadataStandard},
- * which is provided by subclasses in the {@link #getStandard()} method. There is typically
- * a large number of {@code AbstractMetadata} subclasses (not necessarily as direct children)
- * for the same standard.
+ * Provides basic operations using Java reflection for metadata implementations.
+ * All {@code AbstractMetadata} instances shall be associated to a {@link MetadataStandard}.
+ * The metadata standard is given by the {@link #getStandard()} method and is typically a
+ * constant fixed by the subclass.
  *
- * <p>This base class reduces the effort required to implement metadata interface by providing
+ * <p>There is a large number of {@code AbstractMetadata} subclasses (not necessarily as direct children)
+ * for the same standard, where each subclass implement one Java interface defined by the metadata standard.
+ * This base class reduces the effort required to implement those metadata interfaces by providing
  * {@link #equals(Object)}, {@link #hashCode()} and {@link #toString()} implementations.
  * Those methods are implemented using Java reflection for invoking the getter methods
  * defined by the {@code MetadataStandard}.</p>
  *
- * <p>{@code AbstractMetadata} may be read-only or read/write, at implementation choice.
- * The {@link ModifiableMetadata} subclass provides the basis of most SIS metadata classes
- * having writing capabilities.</p>
- *
- * {@section Synchronization}
- * The methods in this class are not synchronized. Synchronizations may be done by getter and
- * setter methods in subclasses, at implementation choice. We never synchronize the methods that
- * perform deep traversal of the metadata tree (like {@code equals(Object)}, {@code hashCode()}
- * or {@code toString()}) because such synchronizations are deadlock prone. For example if
- * subclasses synchronize their getter methods, then many locks may be acquired in various orders.
+ * {@note This class does not synchronize the methods that perform deep traversal of the metadata tree
+ * (like <code>equals(Object)</code>, <code>hashCode()</code> or <code>toString()</code>) because such
+ * synchronizations are deadlock prone. For example if subclasses synchronize their getter methods,
+ * then many locks may be acquired in various orders.}
+ *
+ * {@code AbstractMetadata} subclasses may be read-only or read/write, at implementation choice.
+ * The methods that modify the metadata may throw {@link UnmodifiableMetadataException} if the
+ * metadata does not support the operation. Those methods are:
+ *
+ * <ul>
+ *   <li>{@link #prune()}</li>
+ *   <li>{@link #shallowCopy(Object)}</li>
+ *   <li>{@link #asMap()} with {@code put} operations</li>
+ * </ul>
+ *
+ * Read-only operations operating on metadata values are:
+ *
+ * <ul>
+ *   <li>{@link #isEmpty()}</li>
+ *   <li>{@link #asMap()} with {@code get} operations</li>
+ *   <li>{@link #asTreeTable()}</li>
+ *   <li>{@link #equals(Object, ComparisonMode)}</li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.4)
@@ -57,6 +75,13 @@ public abstract class AbstractMetadata i
     protected static final Logger LOGGER = Logging.getLogger(AbstractMetadata.class);
 
     /**
+     * A view of this metadata as a map. Will be created only when first needed.
+     *
+     * @see #asMap()
+     */
+    private transient Map<String,Object> asMap;
+
+    /**
      * Creates an initially empty metadata.
      */
     protected AbstractMetadata() {
@@ -64,15 +89,12 @@ public abstract class AbstractMetadata i
 
     /**
      * Returns the metadata standard implemented by subclasses.
+     * Subclasses will typically return a hard-coded constant such as
+     * {@link MetadataStandard#ISO_19115}.
      *
      * @return The metadata standard implemented.
-     *
-     * @todo This method returns {@link MetadataStandard#ISO_19115} for now,
-     *       but will become an abstract method soon.
      */
-    public MetadataStandard getStandard() {
-        return MetadataStandard.ISO_19115;
-    }
+    public abstract MetadataStandard getStandard();
 
     /**
      * Returns the metadata interface implemented by this class. It should be one of the
@@ -89,6 +111,105 @@ public abstract class AbstractMetadata i
     }
 
     /**
+     * Returns {@code true} if this metadata contains only {@code null} or empty properties.
+     * A property is considered empty in any of the following cases:
+     *
+     * <ul>
+     *   <li>An empty {@linkplain CharSequence character sequences}.</li>
+     *   <li>An {@linkplain Collection#isEmpty() empty collection} or an empty array.</li>
+     *   <li>A collection or array containing only {@code null} or empty elements.</li>
+     *   <li>An other metadata object containing only {@code null} or empty attributes.</li>
+     * </ul>
+     *
+     * Note that empty properties can be removed by calling the {@link ModifiableMetadata#prune()}
+     * method.
+     *
+     * {@section Note for implementors}
+     * The default implementation uses Java reflection indirectly, by iterating over all entries
+     * returned by {@link MetadataStandard#asMap(Object, KeyNamePolicy, ValueExistencePolicy)}.
+     * Subclasses that override this method should usually not invoke {@code super.isEmpty()},
+     * because the Java reflection will discover and process the properties defined in the
+     * subclasses - which is usually not the intend when overriding a method.
+     *
+     * @return {@code true} if this metadata is empty.
+     *
+     * @see org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#isEmpty()
+     */
+    public boolean isEmpty() {
+        return Pruner.isEmpty(this, false);
+    }
+
+    /**
+     * Removes all references to empty properties. The default implementation iterates over all
+     * {@linkplain ValueExistencePolicy#NON_NULL non null} properties, and sets to {@code null}
+     * the properties for which {@link #isEmpty()} returned {@code true}.
+     *
+     * @throws UnmodifiableMetadataException If this metadata is not modifiable.
+     */
+    public void prune() {
+        Pruner.isEmpty(this, true);
+    }
+
+    /**
+     * Copies the values from the specified metadata. The {@code source} metadata must implements
+     * the same metadata interface (defined by the {@linkplain #getStandard() standard}) than this
+     * class, but doesn't need to be the same implementation class.
+     * The default implementation performs the copy using Java reflections.
+     *
+     * {@note This method is intended to provide the functionality of a <cite>copy constructor</cite>.
+     * We do not provide copy constructor directly because usage of Java reflection in this context
+     * is unsafe (we could invoke subclass methods before the subclasses construction is completed).}
+     *
+     * @param  source The metadata to copy values from.
+     * @throws ClassCastException if the specified metadata doesn't implements the expected
+     *         metadata interface.
+     * @throws UnmodifiableMetadataException if this class doesn't define {@code set*(…)} methods
+     *         corresponding to the {@code get*()} methods found in the implemented interface, or
+     *         if this instance is not modifiable for some other reason.
+     */
+    public void shallowCopy(final Object source) throws ClassCastException, UnmodifiableMetadataException {
+        ensureNonNull("source", source);
+        getStandard().shallowCopy(source, this);
+    }
+
+    /**
+     * Returns a view of the property values in a {@link Map}. The map is backed by this metadata
+     * object, so changes in the underlying metadata object are immediately reflected in the map
+     * and conversely.
+     *
+     * <p>The map supports the {@link Map#put(Object, Object) put(…)} and {@link Map#remove(Object)
+     * remove(…)} operations if the underlying metadata object contains setter methods.
+     * The keys are case-insensitive and can be either the JavaBeans property name or
+     * the UML identifier.</p>
+     *
+     * <p>The default implementation is equivalent to the following method call:</p>
+     * {@preformat java
+     *   return getStandard().asMap(this, KeyNamePolicy.JAVABEANS_PROPERTY, ValueExistencePolicy.NON_EMPTY);
+     * }
+     *
+     * @return A view of this metadata object as a map.
+     *
+     * @see MetadataStandard#asMap(Object, KeyNamePolicy, ValueExistencePolicy)
+     */
+    public synchronized Map<String,Object> asMap() {
+        if (asMap == null) {
+            asMap = getStandard().asMap(this, KeyNamePolicy.JAVABEANS_PROPERTY, ValueExistencePolicy.NON_EMPTY);
+        }
+        return asMap;
+    }
+
+    /**
+     * Returns the property types and values as a tree table.
+     * In the current implementation, the tree is not live (i.e. changes in metadata are not
+     * reflected in the tree). However it may be improved in a future SIS implementation.
+     *
+     * @return The property types and values as a tree table.
+     */
+    public TreeTable asTreeTable() {
+        return getStandard().asTreeTable(this);
+    }
+
+    /**
      * Compares this metadata with the specified object for equality. The default
      * implementation uses Java reflection. Subclasses may override this method
      * for better performances, or for comparing "hidden" properties not specified
@@ -158,10 +279,9 @@ public abstract class AbstractMetadata i
      * similar contract than {@link java.util.Set#hashCode()} and ensures that the hash code
      * value is insensitive to the ordering of properties.
      *
-     * {@section Performance note}
-     * This method does not cache the value because current implementation has no notification
-     * mechanism for tracking changes in children properties. If this metadata is known to be
-     * immutable, then subclasses may consider caching the hash code value at their choice.
+     * {@note This method does not cache the value because current implementation has no notification
+     *        mechanism for tracking changes in children properties. If this metadata is known to be
+     *        immutable, then subclasses may consider caching the hash code value at their choice.}
      *
      * @see MetadataStandard#hashCode(Object)
      */
@@ -169,4 +289,20 @@ public abstract class AbstractMetadata i
     public int hashCode() {
         return getStandard().hashCode(this);
     }
+
+    /**
+     * Returns a string representation of this metadata.
+     * The default implementation is as below:
+     *
+     * {@preformat java
+     *     return asTreeTable().toString();
+     * }
+     *
+     * Note that this make extensive use of Unicode characters
+     * and is better rendered with a monospaced font.
+     */
+    @Override
+    public String toString() {
+        return asTreeTable().toString();
+    }
 }

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java?rev=1453026&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.sis.metadata;
+
+
+/**
+ * Thrown when a metadata is in a invalid state, usually because a mandatory property is missing.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ */
+public class InvalidMetadataException extends IllegalStateException {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 3219759595538181102L;
+
+    /**
+     * Creates a new exception with the specified detail message.
+     *
+     * @param message The detail message.
+     */
+    public InvalidMetadataException(final String message) {
+        super(message);
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/InvalidMetadataException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java?rev=1453026&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -0,0 +1,153 @@
+/*
+ * 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 org.apache.sis.metadata;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+
+
+/**
+ * The base class of {@link Map} views.
+ *
+ * @param <V> The type of values in the map.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.04)
+ * @version 0.3
+ * @module
+ */
+abstract class MetadataMap<V> extends AbstractMap<String,V> {
+    /**
+     * The accessor to use for the metadata.
+     */
+    final PropertyAccessor accessor;
+
+    /**
+     * Determines the string representation of keys in the map.
+     */
+    final KeyNamePolicy keyPolicy;
+
+    /**
+     * A view of the mappings contained in this map.
+     */
+    transient Set<Map.Entry<String,V>> entrySet;
+
+    /**
+     * Creates a new map backed by the given accessor.
+     */
+    MetadataMap(final PropertyAccessor accessor, final KeyNamePolicy keyPolicy) {
+        this.accessor  = accessor;
+        this.keyPolicy = keyPolicy;
+    }
+
+    /**
+     * Returns the number of elements in this map.
+     */
+    @Override
+    public abstract int size();
+
+    /**
+     * Returns a view of the mappings contained in this map. Subclasses shall override this method
+     * if they define a different entries set class than the default {@link Entries} inner class.
+     */
+    @Override
+    public synchronized Set<Map.Entry<String,V>> entrySet() {
+        if (entrySet == null) {
+            entrySet = new Entries();
+        }
+        return entrySet;
+    }
+
+    /**
+     * Returns an iterator over the entries in this map.
+     */
+    abstract Iterator<Map.Entry<String,V>> iterator();
+
+
+
+
+    /**
+     * The iterator over the elements contained in a {@link Entries} set.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-3.04)
+     * @version 0.3
+     * @module
+     */
+    abstract class Iter implements Iterator<Map.Entry<String,V>> {
+        /**
+         * Creates a new iterator.
+         */
+        Iter() {
+        }
+
+        /**
+         * Assumes that the underlying map is unmodifiable.
+         * Only {@link PropertyMap} supports this method.
+         */
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+
+
+
+    /**
+     * Base class of views of the entries contained in the map.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-3.04)
+     * @version 0.3
+     * @module
+     */
+    class Entries extends AbstractSet<Map.Entry<String,V>> {
+        /**
+         * Creates a new entries set.
+         */
+        Entries() {
+        }
+
+        /**
+         * Returns true if this collection contains no elements.
+         */
+        @Override
+        public final boolean isEmpty() {
+            return MetadataMap.this.isEmpty();
+        }
+
+        /**
+         * Returns the number of elements in this collection.
+         */
+        @Override
+        public final int size() {
+            return MetadataMap.this.size();
+        }
+
+        /**
+         * Returns an iterator over the elements contained in this collection.
+         */
+        @Override
+        public final Iterator<Map.Entry<String,V>> iterator() {
+            return MetadataMap.this.iterator();
+        }
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java?rev=1453026&r1=1453025&r2=1453026&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataStandard.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -27,6 +27,7 @@ import org.opengis.metadata.citation.Cit
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.collection.TreeTable;
 import org.apache.sis.internal.util.SystemListener;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -419,6 +420,56 @@ public class MetadataStandard {
     }
 
     /**
+     * Returns a view of the specified metadata object as a {@link Map}.
+     * The map is backed by the metadata object using Java reflection, so changes in the
+     * underlying metadata object are immediately reflected in the map and conversely.
+     *
+     * <p>The map content is determined by the arguments: {@code metadata} determines the set of
+     * keys, {@code keyPolicy} determines their {@code String} representations of those keys and
+     * {@code valuePolicy} determines whether entries having a null value or an empty collection
+     * shall be included in the map.</p>
+     *
+     * <p>The map supports the {@link Map#put(Object, Object) put(…)} and {@link Map#remove(Object)
+     * remove(…)} operations if the underlying metadata object contains setter methods.
+     * The keys are case-insensitive and can be either the JavaBeans property name or
+     * the UML identifier.</p>
+     *
+     * @param  metadata The metadata object to view as a map.
+     * @param  keyPolicy Determines the string representation of map keys.
+     * @param  valuePolicy Whether the entries having null value or empty collection shall be
+     *         included in the map.
+     * @return A map view over the metadata object.
+     * @throws ClassCastException if the metadata object doesn't implement a metadata
+     *         interface of the expected package.
+     *
+     * @see AbstractMetadata#asMap()
+     */
+    public Map<String,Object> asMap(final Object metadata, final KeyNamePolicy keyPolicy,
+            final ValueExistencePolicy valuePolicy) throws ClassCastException
+    {
+        ensureNonNull("metadata",    metadata);
+        ensureNonNull("keyPolicy",   keyPolicy);
+        ensureNonNull("valuePolicy", valuePolicy);
+        return new PropertyMap(metadata, getAccessor(metadata.getClass(), true), keyPolicy, valuePolicy);
+    }
+
+    /**
+     * Returns the specified metadata object as a tree table.
+     * In the current implementation, the tree is not live (i.e. changes in metadata are not
+     * reflected in the tree). However it may be improved in a future SIS implementation.
+     *
+     * @param  metadata The metadata object to formats as a tree table.
+     * @return A tree table representation of the specified metadata.
+     * @throws ClassCastException if the metadata object doesn't implement a metadata
+     *         interface of the expected package.
+     *
+     * @see AbstractMetadata#asTreeTable()
+     */
+    public TreeTable asTreeTable(final Object metadata) throws ClassCastException {
+        throw new UnsupportedOperationException("Not yet implemented"); // TODO
+    }
+
+    /**
      * Replaces every properties in the specified metadata by their
      * {@linkplain ModifiableMetadata#unmodifiable() unmodifiable variant}.
      *

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java?rev=1453026&r1=1453025&r2=1453026&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -35,13 +35,9 @@ import static org.apache.sis.util.collec
 
 
 /**
- * Base class for metadata that may (or may not) be modifiable. Implementations will typically
- * provide {@code set*(...)} methods for each corresponding {@code get*()} method. An initially
- * modifiable metadata may become unmodifiable at a later stage (typically after its construction
- * is completed) by the call to the {@link #freeze()} method.
- *
- * {@section Guidline for implementors}
- * Subclasses should follow the pattern below for every {@code get} and {@code set} methods,
+ * Provides convenience methods for support of modifiable properties in metadata implementations.
+ * Implementations typically provide {@code set*(…)} methods for each corresponding {@code get*()}
+ * method. Subclasses can follow the pattern below for every {@code get} and {@code set} methods,
  * with a different processing for singleton value or for {@linkplain Collection collections}.
  *
  * <p>For singleton value:</p>
@@ -77,6 +73,10 @@ import static org.apache.sis.util.collec
  *     }
  * }
  *
+ * An initially modifiable metadata may become unmodifiable at a later stage
+ * (typically after its construction is completed) by the call to the
+ * {@link #freeze()} method.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.1)
  * @version 0.3
@@ -600,15 +600,13 @@ public abstract class ModifiableMetadata
 
     /**
      * Returns a shallow copy of this metadata.
-     *
-     * {@section Usage}
-     * While {@linkplain Cloneable cloneable}, this class do not provides the {@code clone()}
+     * While {@linkplain Cloneable cloneable}, this class does not provides the {@code clone()}
      * operation as part of the public API. The clone operation is required for the internal
-     * working of the {@link #unmodifiable()} method, which needs <strong>shallow</strong>
+     * working of the {@link #unmodifiable()} method, which needs <em>shallow</em>
      * copies of metadata entities. The default {@link Object#clone()} implementation is
      * sufficient in most cases.
      *
-     * @return A <strong>shallow</strong> copy of this metadata.
+     * @return A <em>shallow</em> copy of this metadata.
      * @throws CloneNotSupportedException if the clone is not supported.
      */
     @Override

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java?rev=1453026&r1=1453025&r2=1453026&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -550,14 +550,14 @@ final class PropertyAccessor {
      * Returns the name of the property at the given index, or {@code null} if none.
      *
      * @param  index The index of the property for which to get the name.
-     * @param  keyName The kind of name to return.
+     * @param  keyPolicy The kind of name to return.
      * @return The name of the given kind at the given index,
      *         or {@code null} if the index is out of bounds.
      */
     @SuppressWarnings("fallthrough")
-    final String name(final int index, final KeyNamePolicy keyName) {
+    final String name(final int index, final KeyNamePolicy keyPolicy) {
         if (index >= 0 && index < names.length) {
-            switch (keyName) {
+            switch (keyPolicy) {
                 case UML_IDENTIFIER: {
                     final UML uml = getters[index].getAnnotation(UML.class);
                     if (uml != null) {
@@ -675,7 +675,13 @@ final class PropertyAccessor {
 
     /**
      * Returns the value for the specified metadata, or {@code null} if none.
+     * If the given index is out of bounds, then this method returns {@code null},
+     * so it is safe to invoke this method even if {@link #indexOf(String, boolean)}
+     * returned -1.
      *
+     * @param  index The index of the property for which to get a value.
+     * @param  metadata The metadata object to query.
+     * @return The value, or {@code null} if none or if the given is out of bounds.
      * @throws BackingStoreException If the implementation threw a checked exception.
      */
     final Object get(final int index, final Object metadata) throws BackingStoreException {
@@ -719,6 +725,12 @@ final class PropertyAccessor {
      * a new collection or map before the new value is set, because the setter methods typically
      * copy the new collection in their existing instance.
      *
+     * <p>If the given index is out of bounds, then this method does nothing and return {@code null}.
+     * We do that because the {@link PropertyMap#remove(Object)} method may invoke this method with
+     * an index of -1 if the {@link #indexOf(String, boolean)} method didn't found the property name.
+     * However the given value will be silently discarded, so index out-of-bounds shall be used only
+     * in the context of {@code remove} operations (this is not verified).</p>
+     *
      * @param  index    The index of the property to set.
      * @param  metadata The metadata object on which to set the value.
      * @param  value    The new value.
@@ -731,7 +743,10 @@ final class PropertyAccessor {
     final Object set(final int index, final Object metadata, final Object value, final boolean getOld)
             throws UnmodifiableMetadataException, ClassCastException, BackingStoreException
     {
-        if (index >= 0 && index < standardCount && setters != null) {
+        if (index < 0 || index >= standardCount) {
+            return null;
+        }
+        if (setters != null) {
             final Method getter = getters[index];
             final Method setter = setters[index];
             if (setter != null) {
@@ -1088,6 +1103,7 @@ final class PropertyAccessor {
     /**
      * Counts the number of non-empty properties.
      *
+     * @param  max Stop the count if we reach that value.
      * @throws BackingStoreException If the implementation threw a checked exception.
      */
     public int count(final Object metadata, final int max) throws BackingStoreException {

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java?rev=1453026&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -0,0 +1,404 @@
+/*
+ * 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 org.apache.sis.metadata;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.apache.sis.util.CharSequences;
+
+// Related to JDK7
+import java.util.Objects;
+
+
+/**
+ * A view of a metadata object as a map. Keys are property names and values
+ * are the value returned by the {@code getFoo()} method using reflection.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-2.4)
+ * @version 0.3
+ * @module
+ *
+ * @see MetadataStandard#asMap(Object, KeyNamePolicy, ValueExistencePolicy)
+ */
+final class PropertyMap extends MetadataMap<Object> {
+    /**
+     * The metadata object to wrap.
+     */
+    private final Object metadata;
+
+    /**
+     * The behavior of this map toward null or empty values.
+     */
+    final ValueExistencePolicy valuePolicy;
+
+    /**
+     * Creates a property map for the specified metadata and accessor.
+     *
+     * @param metadata    The metadata object to wrap.
+     * @param accessor    The accessor to use for the metadata.
+     * @param keyPolicy   Determines the string representation of keys in the map..
+     * @param valuePolicy The behavior of this map toward null or empty values.
+     */
+    PropertyMap(final Object metadata, final PropertyAccessor accessor,
+            final KeyNamePolicy keyPolicy, final ValueExistencePolicy valuePolicy)
+    {
+        super(accessor, keyPolicy);
+        this.metadata    = metadata;
+        this.valuePolicy = valuePolicy;
+    }
+
+    /**
+     * Returns {@code true} if the given value should be ignored.
+     */
+    final boolean ignore(final Object value) {
+        return valuePolicy == ValueExistencePolicy.NON_EMPTY && PropertyAccessor.isNullOrEmpty(value);
+    }
+
+    /**
+     * Returns {@code true} if this map contains no key-value mappings.
+     */
+    @Override
+    public boolean isEmpty() {
+        return accessor.count(metadata, 1) == 0;
+    }
+
+    /**
+     * Returns the number of elements in this map.
+     */
+    @Override
+    public int size() {
+        return accessor.count(metadata, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Returns {@code true} if this map contains a mapping for the specified key.
+     */
+    @Override
+    public boolean containsKey(final Object key) {
+        return get(key) != null;
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped, or {@code null}
+     * if this map contains no mapping for the key.
+     */
+    @Override
+    public Object get(final Object key) {
+        if (key instanceof String) {
+            final Object value = accessor.get(accessor.indexOf((String) key, false), metadata);
+            if (!ignore(value)) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Associates the specified value with the specified key in this map.
+     *
+     * @throws IllegalArgumentException if the given key is not the name of a property in the metadata.
+     * @throws ClassCastException if the given value is not of the expected type.
+     * @throws UnmodifiableMetadataException if the attribute for the given key is read-only.
+     */
+    @Override
+    public Object put(final String key, final Object value) {
+        final Object old = accessor.set(accessor.indexOf(key, true), metadata, value, true);
+        return ignore(old) ? null : old;
+    }
+
+    /**
+     * Puts every entries from the given map. This method is overloaded for performance reasons
+     * since we are not interested in the return value of the {@link #put(String, Object)} method.
+     *
+     * @throws IllegalArgumentException if at least one key is not the name of a property in the metadata.
+     * @throws ClassCastException if at least one value is not of the expected type.
+     * @throws UnmodifiableMetadataException if at least one attribute is read-only.
+     */
+    @Override
+    public void putAll(final Map<? extends String, ?> map) {
+        for (final Map.Entry<? extends String, ?> e : map.entrySet()) {
+            accessor.set(accessor.indexOf(e.getKey(), true), metadata, e.getValue(), false);
+        }
+    }
+
+    /**
+     * Removes the mapping for a key from this map if it is present.
+     *
+     * @throws UnmodifiableMetadataException if the attribute for the given key is read-only.
+     */
+    @Override
+    public Object remove(final Object key) throws UnsupportedOperationException {
+        if (key instanceof String) {
+            final Object old = accessor.set(accessor.indexOf((String) key, false), metadata, null, true);
+            if (!ignore(old)) {
+                return old;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a view of the mappings contained in this map.
+     */
+    @Override
+    public synchronized Set<Map.Entry<String,Object>> entrySet() {
+        if (entrySet == null) {
+            entrySet = new Entries();
+        }
+        return entrySet;
+    }
+
+    /**
+     * Returns an iterator over the entries contained in this map.
+     */
+    @Override
+    final Iterator<Map.Entry<String,Object>> iterator() {
+        return new Iter();
+    }
+
+
+
+
+    /**
+     * A map entry for a given property.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-2.4)
+     * @version 0.3
+     * @module
+     */
+    final class Property implements Map.Entry<String,Object> {
+        /**
+         * The property index.
+         */
+        final int index;
+
+        /**
+         * Creates an entry for the property at the given index.
+         */
+        Property(final int index) {
+            this.index = index;
+        }
+
+        /**
+         * Returns the key corresponding to this entry.
+         */
+        @Override
+        public String getKey() {
+            return accessor.name(index, keyPolicy);
+        }
+
+        /**
+         * Returns value type as declared in the interface method signature.
+         * It may be a primitive type.
+         */
+        public Class<?> getValueType() {
+            return accessor.type(index, TypeValuePolicy.PROPERTY_TYPE);
+        }
+
+        /**
+         * Returns the value corresponding to this entry.
+         */
+        @Override
+        public Object getValue() {
+            final Object value = accessor.get(index, metadata);
+            return ignore(value) ? null : value;
+        }
+
+        /**
+         * Replaces the value corresponding to this entry with the specified value.
+         *
+         * @throws ClassCastException if the given value is not of the expected type.
+         * @throws UnmodifiableMetadataException if the attribute for this entry is read-only.
+         */
+        @Override
+        public Object setValue(final Object value) {
+            return accessor.set(index, metadata, value, true);
+        }
+
+        /**
+         * Compares the specified object with this entry for equality.
+         * Criterion are specified by the {@link Map.Entry} contract.
+         */
+        @Override
+        public boolean equals(final Object object) {
+            if (object instanceof Map.Entry<?,?>) {
+                final Map.Entry<?,?> entry = (Map.Entry<?,?>) object;
+                return Objects.equals(getKey(),   entry.getKey()) &&
+                       Objects.equals(getValue(), entry.getValue());
+            }
+            return false;
+        }
+
+        /**
+         * Returns the hash code value for this map entry. The
+         * formula is specified by the {@link Map.Entry} contract.
+         */
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
+        }
+
+        /**
+         * Returns a string representation of this entry.
+         * This method is mostly for debugging purpose.
+         */
+        @Override
+        public String toString() {
+            String value = String.valueOf(getValue());
+            value = value.substring(0, CharSequences.indexOfLineStart(value, 1, 0));
+            return getKey() + '=' + value;
+        }
+    }
+
+
+
+
+    /**
+     * The iterator over the {@link Property} elements contained in an {@link Entries} set.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-2.4)
+     * @version 0.3
+     * @module
+     */
+    private final class Iter extends MetadataMap<Object>.Iter {
+        /**
+         * The current and the next property, or {@code null} if the iteration is over.
+         */
+        private Property current, next;
+
+        /**
+         * Creates en iterator.
+         */
+        Iter() {
+            move(0);
+        }
+
+        /**
+         * Moves {@link #next} to the first property with a valid value,
+         * starting at the specified index.
+         */
+        private void move(int index) {
+            final int count = accessor.count();
+            while (index < count) {
+                final Object value = accessor.get(index, metadata);
+                final boolean skip;
+                switch (valuePolicy) {
+                    case ALL: {
+                        skip = false; // Never skip entries.
+                        break;
+                    }
+                    case NON_NULL: {
+                        skip = (value == null); // Skip only null values (not empty collections).
+                        break;
+                    }
+                    case NON_EMPTY: {
+                        skip = PropertyAccessor.isNullOrEmpty(value);
+                        break;
+                    }
+                    default: {
+                        throw new AssertionError(valuePolicy);
+                    }
+                }
+                if (!skip) {
+                    next = new Property(index);
+                    return;
+                }
+                index++;
+            }
+            next = null;
+        }
+
+        /**
+         * Returns {@code true} if the iteration has more elements.
+         */
+        @Override
+        public boolean hasNext() {
+            return next != null;
+        }
+
+        /**
+         * Returns the next element in the iteration.
+         */
+        @Override
+        public Map.Entry<String,Object> next() {
+            if (next != null) {
+                current = next;
+                move(next.index + 1);
+                return current;
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        /**
+         * Removes from the underlying collection the last element returned by the iterator.
+         *
+         * @throws UnmodifiableMetadataException if the attribute for this entry is read-only.
+         */
+        @Override
+        public void remove() {
+            if (current != null) {
+                current.setValue(null);
+                current = null;
+            } else {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+
+
+
+    /**
+     * View of the entries contained in the map.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3 (derived from geotk-2.4)
+     * @version 0.3
+     * @module
+     */
+    private final class Entries extends MetadataMap<Object>.Entries {
+        /**
+         * Creates an entry set.
+         */
+        Entries() {
+        }
+
+        /**
+         * Returns {@code true} if this collection contains the specified element.
+         */
+        @Override
+        public boolean contains(final Object object) {
+            if (object instanceof Map.Entry<?,?>) {
+                final Map.Entry<?,?> entry = (Map.Entry<?,?>) object;
+                final Object key = entry.getKey();
+                if (key instanceof String) {
+                    final int index = accessor.indexOf((String) key, false);
+                    if (index >= 0) {
+                        return new Property(index).equals(entry);
+                    }
+                }
+            }
+            return false;
+        }
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyMap.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java?rev=1453026&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -0,0 +1,197 @@
+/*
+ * 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 org.apache.sis.metadata;
+
+import java.util.Map;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import org.opengis.util.CodeList;
+import org.apache.sis.util.collection.CollectionsExt;
+
+
+/**
+ * Implementation of {@link AbstractMetadata#isEmpty()} and {@link ModifiableMetadata#prune()}
+ * methods.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.20)
+ * @version 0.3
+ * @module
+ */
+final class Pruner extends ThreadLocal<Map<Object,Boolean>> {
+    /**
+     * The thread-local map of metadata object already tested.
+     */
+    private static final Pruner INSTANCE = new Pruner();
+
+    /**
+     * For internal usage only.
+     */
+    private Pruner() {
+    }
+
+    /**
+     * Creates an initially empty hash map when the {@code isEmpty()} or {@code prune()}
+     * method is invoked, before any recursive invocation.
+     */
+    @Override
+    protected Map<Object,Boolean> initialValue() {
+        return new IdentityHashMap<>();
+    }
+
+    /**
+     * Returns the metadata properties. When used for pruning empty values, the map needs to
+     * include empty (but non-null) values in order to allow us to set them to {@code null}.
+     */
+    private static Map<String, Object> asMap(final MetadataStandard standard, final Object metadata, final boolean prune) {
+        return standard.asMap(metadata, KeyNamePolicy.JAVABEANS_PROPERTY, prune ?
+                ValueExistencePolicy.NON_NULL : ValueExistencePolicy.NON_EMPTY);
+    }
+
+    /**
+     * Returns {@code true} if the value for the given entry is a primitive type.
+     */
+    private static boolean isPrimitive(final Map.Entry<String,Object> entry) {
+        return (entry instanceof PropertyMap.Property) && ((PropertyMap.Property) entry).getValueType().isPrimitive();
+    }
+
+    /**
+     * Returns {@code true} if all properties in the given metadata are null or empty.
+     * This method is the entry point for the {@link AbstractMetadata#isEmpty()} and
+     * {@link ModifiableMetadata#prune()} public methods.
+     *
+     * <p>This method is typically invoked recursively while we iterate down the metadata tree.
+     * It creates a map of visited nodes when the iteration begin, and deletes that map when the
+     * iteration ends.</p>
+     *
+     * @param  metadata The metadata object.
+     * @param  prune {@code true} for deleting empty entries.
+     * @return {@code true} if all metadata properties are null or empty.
+     */
+    static boolean isEmpty(final AbstractMetadata metadata, final boolean prune) {
+        final Map<String,Object> properties = asMap(metadata.getStandard(), metadata, prune);
+        final Map<Object,Boolean> tested = INSTANCE.get();
+        if (!tested.isEmpty()) {
+            return isEmpty(properties, tested, prune);
+        } else try {
+            tested.put(metadata, Boolean.FALSE);
+            return isEmpty(properties, tested, prune);
+        } finally {
+            INSTANCE.remove();
+        }
+    }
+
+    /**
+     * {@link #isEmpty(boolean)} implementation, potentially invoked recursively for inspecting
+     * child metadata and optionally removing empty ones. The map given in argument is a safety
+     * guard against infinite recursivity.
+     *
+     * @param  properties The metadata properties.
+     * @param  tested An initially singleton map, to be filled with tested metadata.
+     * @param  prune {@code true} for removing empty properties.
+     * @return {@code true} if all metadata properties are null or empty.
+     */
+    private static boolean isEmpty(final Map<String,Object> properties,
+            final Map<Object,Boolean> tested, final boolean prune)
+    {
+        boolean isEmpty = true;
+        for (final Map.Entry<String,Object> entry : properties.entrySet()) {
+            final Object value = entry.getValue();
+            /*
+             * No need to check for null values, because the ValueExistencePolicy argument
+             * given to asMap(…) asked for non-null values. If nevertheless a value is null,
+             * following code should be robust to that.
+             *
+             * We use the 'tested' map in order to avoid computing the same value twice, but
+             * also as a check against infinite recursivity - which is why a value needs to be
+             * set before to iterate over children. The default value is 'false' because if we
+             * test the same object through a "A → B → A" dependency chain, this means that A
+             * was not empty (since it contains B).
+             */
+            final Boolean isEntryEmpty = tested.put(value, Boolean.FALSE);
+            if (isEntryEmpty != null) {
+                if (isEntryEmpty) { // If a value was already set, restore the original value.
+                    tested.put(value, Boolean.TRUE);
+                } else {
+                    isEmpty = false;
+                }
+            } else {
+                boolean allEmpty = true;
+                final Collection<?> values = CollectionsExt.toCollection(value);
+                for (final Iterator<?> it = values.iterator(); it.hasNext();) {
+                    final Object element = it.next();
+                    if (!PropertyAccessor.isNullOrEmpty(element)) {
+                        /*
+                         * If the value is not an empty "simple" property (null value, or empty
+                         * string, or an empty collection or array), check if it is an other
+                         * metadata element. If so, invoke the isEmpty() method recursively.
+                         */
+                        final boolean e;
+                        if (element instanceof Enum<?> || element instanceof CodeList<?>) {
+                            e = false;
+                        } else if (element instanceof AbstractMetadata) {
+                            final AbstractMetadata md = (AbstractMetadata) element;
+                            if (prune) md.prune();
+                            e = md.isEmpty();
+                        } else {
+                            final MetadataStandard standard = MetadataStandard.forClass(element.getClass());
+                            if (standard != null) {
+                                e = isEmpty(asMap(standard, element, prune), tested, prune);
+                            } else if (isPrimitive(entry)) {
+                                if (value instanceof Number) {
+                                    e = Double.isNaN(((Number) value).doubleValue());
+                                } else {
+                                    // Typically methods of the kind 'isFooAvailable()'.
+                                    e = Boolean.FALSE.equals(value);
+                                }
+                            } else {
+                                e = false; // Element is a String, Number (not primitive), etc.
+                            }
+                        }
+                        if (!e) {
+                            // At this point, we have determined that the property is not empty.
+                            // If we are not removing empty nodes, there is no need to continue.
+                            if (!prune) {
+                                return false;
+                            }
+                            allEmpty = false;
+                            continue;
+                        }
+                    }
+                    // Found an empty element. Remove it if we are
+                    // allowed to do so, then check next elements.
+                    if (prune && values == value) {
+                        it.remove();
+                    }
+                }
+                // If all elements were empty, set the whole property to 'null'.
+                if (allEmpty) {
+                    tested.put(value, Boolean.TRUE);
+                    if (prune) try {
+                        entry.setValue(null);
+                    } catch (UnsupportedOperationException e) {
+                        // Entry is read only - ignore.
+                    }
+                } else {
+                    isEmpty = false;
+                }
+            }
+        }
+        return isEmpty;
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/TypeValuePolicy.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/TypeValuePolicy.java?rev=1453026&r1=1453025&r2=1453026&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/TypeValuePolicy.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/TypeValuePolicy.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -18,9 +18,9 @@ package org.apache.sis.metadata;
 
 
 /**
- * Whatever {@link MetadataStandard#asTypeMap MetadataStandard.asTypeMap(…)} shall return values
- * for the property types, the element types (same as property types except for collections) or
- * the declaring classes.
+ * The kind of values in the {@link MetadataStandard#asTypeMap MetadataStandard.asTypeMap(…)}.
+ * This enumeration specifies whether the values shall be property types, element types (same
+ * as property types except for collections) or the declaring classes.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.03)

Added: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java?rev=1453026&view=auto
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java (added)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -0,0 +1,56 @@
+/*
+ * 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 org.apache.sis.metadata.iso;
+
+import java.io.Serializable;
+import net.jcip.annotations.ThreadSafe;
+import org.apache.sis.metadata.MetadataStandard;
+import org.apache.sis.metadata.ModifiableMetadata;
+
+
+/**
+ * The base class of ISO 19115 implementation classes. Each sub-classes implements one
+ * of the ISO Metadata interface provided by <a href="http://www.geoapi.org">GeoAPI</a>.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-2.1)
+ * @version 0.3
+ * @module
+ */
+@ThreadSafe
+public class ISOMetadata extends ModifiableMetadata implements Serializable {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 5730550742604669102L;
+
+    /**
+     * Constructs an initially empty metadata.
+     */
+    protected ISOMetadata() {
+        super();
+    }
+
+    /**
+     * Returns the metadata standard implemented by subclasses,
+     * which is {@linkplain MetadataStandard#ISO_19115 ISO 19115}.
+     */
+    @Override
+    public MetadataStandard getStandard() {
+        return MetadataStandard.ISO_19115;
+    }
+}

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/ISOMetadata.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java?rev=1453026&r1=1453025&r2=1453026&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java [UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/DefaultCitation.java [UTF-8] Tue Mar  5 21:48:25 2013
@@ -18,7 +18,6 @@ package org.apache.sis.metadata.iso.cita
 
 import java.util.Date;
 import java.util.Collection;
-
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.citation.CitationDate;
@@ -26,8 +25,7 @@ import org.opengis.metadata.citation.Pre
 import org.opengis.metadata.citation.ResponsibleParty;
 import org.opengis.metadata.citation.Series;
 import org.opengis.util.InternationalString;
-
-import org.apache.sis.metadata.ModifiableMetadata;
+import org.apache.sis.metadata.iso.ISOMetadata;
 
 
 /**
@@ -39,7 +37,7 @@ import org.apache.sis.metadata.Modifiabl
  * @version 0.3
  * @module
  */
-public class DefaultCitation extends ModifiableMetadata implements Citation {
+public class DefaultCitation extends ISOMetadata implements Citation {
     /**
      * Serial number for inter-operability with different versions.
      */



Mime
View raw message