sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1608957 - in /sis/branches/JDK8/core/sis-utility/src: main/java/org/apache/sis/util/iso/ test/java/org/apache/sis/util/iso/
Date Tue, 08 Jul 2014 21:40:58 GMT
Author: desruisseaux
Date: Tue Jul  8 21:40:57 2014
New Revision: 1608957

URL: http://svn.apache.org/r1608957
Log:
Initial DefaultRecord implementation. This commit implied a reorganization of DefaultRecortType internal
for making it more convenient to DefaultRecord. We also tried to make implementations Serializable, but
DefaultRecordSchema appears to be a blocking point (difficult to make it Serializable in a reliable way).

Added:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java   (with props)
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java   (with props)
Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultMemberName.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultScopedName.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultLocalName.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -309,7 +309,7 @@ public class DefaultLocalName extends Ab
      * Invoked by {@link #hashCode()} for computing the hash code value when first needed.
      */
     @Override
-    final int computeHashCode() {
+    int computeHashCode() {
         return Objects.hash(scope, name) ^ (int) serialVersionUID;
     }
 

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultMemberName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultMemberName.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultMemberName.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultMemberName.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -24,6 +24,9 @@ import org.opengis.util.TypeName;
 
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 
+// Branch-dependent imports
+import java.util.Objects;
+
 
 /**
  * The name to identify a member of a {@linkplain org.opengis.util.Record record}.
@@ -41,7 +44,7 @@ import static org.apache.sis.util.Argume
  *
  * @author  Guilhem Legal (Geomatys)
  * @since   0.3 (derived from geotk-3.17)
- * @version 0.3
+ * @version 0.5
  * @module
  */
 @XmlRootElement(name = "MemberName")
@@ -87,4 +90,23 @@ public class DefaultMemberName extends D
     public TypeName getAttributeType() {
         return attributeType;
     }
+
+    /**
+     * Compares this member name with the specified object for equality.
+     *
+     * @param object The object to compare with this name for equality.
+     * @return {@code true} if the given object is equal to this name.
+     */
+    @Override
+    public boolean equals(final Object object) {
+        return super.equals(object) && Objects.equals(attributeType, ((DefaultMemberName) object).attributeType);
+    }
+
+    /**
+     * Invoked by {@link #hashCode()} for computing the hash code value when first needed.
+     */
+    @Override
+    final int computeHashCode() {
+        return super.computeHashCode() + Objects.hashCode(attributeType);
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultNameSpace.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -78,8 +78,10 @@ public class DefaultNameSpace implements
      * We don't use direct reference to {@code GLOBAL} because {@code null} is used as a sentinel
      * value for stopping iterative searches (using GLOBAL would have higher risk of never-ending
      * loops in case of bug), and in order to reduce the stream size during serialization.
+     *
+     * @see #parent()
      */
-    final DefaultNameSpace parent;
+    private final DefaultNameSpace parent;
 
     /**
      * The name of this namespace, usually as a {@link String} or an {@link InternationalString}.
@@ -122,8 +124,7 @@ public class DefaultNameSpace implements
     private transient WeakValueHashMap<String,Object> childs;
 
     /**
-     * Creates the global namespace. This constructor can be invoked by {@link GlobalNameSpace}
-     * only.
+     * Creates the global namespace. This constructor can be invoked by {@link GlobalNameSpace} only.
      */
     DefaultNameSpace() {
         this.parent        = null;
@@ -151,7 +152,7 @@ public class DefaultNameSpace implements
     protected DefaultNameSpace(final DefaultNameSpace parent, CharSequence name,
                                final String headSeparator, final String separator)
     {
-        this.parent = parent;
+        this.parent = (parent != GlobalNameSpace.GLOBAL) ? parent : null;
         ensureNonNull("name",          name);
         ensureNonNull("headSeparator", headSeparator);
         ensureNonNull("separator",     separator);
@@ -248,6 +249,13 @@ public class DefaultNameSpace implements
     }
 
     /**
+     * Returns the parent namespace, replacing null parent by {@link GlobalNameSpace#GLOBAL}.
+     */
+    final DefaultNameSpace parent() {
+        return (parent != null) ? parent : GlobalNameSpace.GLOBAL;
+    }
+
+    /**
      * Returns the depth of the given namespace.
      *
      * @param ns The namespace for which to get the depth, or {@code null}.
@@ -398,7 +406,7 @@ public class DefaultNameSpace implements
                 }
             }
         }
-        assert child.parent == this;
+        assert child.parent() == this;
         return child;
     }
 
@@ -518,7 +526,7 @@ public class DefaultNameSpace implements
      * @return The unique instance.
      */
     Object readResolve() {
-        final DefaultNameSpace p = (parent != null) ? parent : GlobalNameSpace.GLOBAL;
+        final DefaultNameSpace p = parent();
         final String key = key(name);
         final WeakValueHashMap<String,Object> pool = p.childs;
         synchronized (pool) {

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java?rev=1608957&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -0,0 +1,247 @@
+/*
+ * 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.util.iso;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.io.Serializable;
+import org.opengis.util.MemberName;
+import org.opengis.util.Record;
+import org.opengis.util.RecordType;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * A list of logically related elements as (<var>name</var>, <var>value</var>) pairs in a dictionary.
+ *
+ * {@section Limitations}
+ * <ul>
+ *   <li><b>Multi-threading:</b> {@code DefaultRecord} instances are <strong>not</strong> thread-safe.
+ *       Synchronization, if needed, shall be done externally by the caller.</li>
+ *   <li><b>Serialization:</b> this class is serializable if the associated {@code RecordType} and all
+ *        values are also serializable. Note in particular that {@link DefaultRecordSchema} is currently
+ *        <strong>not</strong> serializable, so users wanting serialization may need to define their own
+ *        schema implementation.</li>
+ * </ul>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+public class DefaultRecord implements Record, Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -5293250754663538325L;
+
+    /**
+     * The type definition of this record.
+     */
+    final RecordDefinition definition;
+
+    /**
+     * The record values.
+     */
+    private final Object[] values;
+
+    /**
+     * Creates a new record for the given record type.
+     *
+     * @param type The type definition of the new record.
+     */
+    public DefaultRecord(final RecordType type) {
+        ArgumentChecks.ensureNonNull("type", type);
+        if (type instanceof RecordDefinition) {
+            definition = (RecordDefinition) type;
+        } else {
+            definition = new RecordDefinition.Adapter(type);
+        }
+        values = new Object[definition.size()];
+    }
+
+    /**
+     * Returns the type definition of this record.
+     *
+     * @return The type definition of this record.
+     */
+    @Override
+    public RecordType getRecordType() {
+        return definition.getRecordType();
+    }
+
+    /**
+     * Returns the dictionary of all (<var>name</var>, <var>value</var>) pairs in this record.
+     * This method returns a view which will delegate all {@get} and {@code pt} operations to
+     * the {@link #locate(MemberName)} and {@link #set(MemberName, Object)} methods respectively.
+     *
+     * @return The dictionary of all (<var>name</var>, <var>value</var>) pairs in this record.
+     *
+     * @see RecordType#getMemberTypes()
+     */
+    @Override
+    public Map<MemberName, Object> getAttributes() {
+        return new AbstractMap<MemberName, Object>() {
+            /** Returns the number of members in the record. */
+            @Override
+            public int size() {
+                return definition.size();
+            }
+
+            /** Delegates to {@code DefaultRecord.locate(name)}. */
+            @Override
+            public Object get(final Object name) {
+                return (name instanceof MemberName) ? locate((MemberName) name) : null;
+            }
+
+            /** Delegates to {@code DefaultRecord.set(name, value)}. */
+            @Override
+            public Object put(final MemberName name, final Object value) {
+                final Object previous = locate(name);
+                set(name, value);
+                return previous;
+            }
+
+            /** Returns a set containing all (<var>name</var>, <var>value</var>) pairs in the record. */
+            @Override
+            public Set<Map.Entry<MemberName, Object>> entrySet() {
+                return new Entries();
+            }
+        };
+    }
+
+    /**
+     * The set of map entries to be returned by {@code DefaultRecord.getAttributes().entrySet()}.
+     * {@link AbstractMap} uses this set for providing a default implementation of most methods.
+     */
+    private final class Entries extends AbstractSet<Map.Entry<MemberName,Object>> {
+        /** Returns the number of members in the record. */
+        @Override
+        public int size() {
+            return definition.size();
+        }
+
+        /** Returns an iterator over all record members. */
+        @Override
+        public Iterator<Map.Entry<MemberName, Object>> iterator() {
+            return new Iter();
+        }
+    }
+
+    /**
+     * The iterator to be returned by {@code DefaultRecord.getAttributes().entrySet().iterator()}.
+     * {@link AbstractMap} (indirectly) and {@link AbstractSet} use this iterator for providing a
+     * default implementation of most methods.
+     */
+    private final class Iter implements Iterator<Map.Entry<MemberName,Object>> {
+        /** Index of the next record member to return in the iteration. */
+        private int index;
+
+        /** Returns {@code true} if there is more record members to iterate over. */
+        @Override
+        public boolean hasNext() {
+            return index < definition.size();
+        }
+
+        /** Returns an entry containing the name and value of the next record member. */
+        @Override
+        public Map.Entry<MemberName, Object> next() {
+            if (hasNext()) {
+                return new Entry(index);
+            }
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * A single entry in the map returned by {@code DefaultRecord.getAttributes()}.
+     * Operations on this entry delegate to {@link DefaultRecord#locate(MemberName)}
+     * and {@link DefaultRecord#set(MemberName, Object)} methods.
+     */
+    private final class Entry implements Map.Entry<MemberName,Object> {
+        /** Index of the record member represented by this entry. */
+        private final int index;
+
+        /** Creates a new entry for the record member at the given index. */
+        Entry(final int index) {
+            this.index = index;
+        }
+
+        /** Returns the name of the record member contained in this entry. */
+        @Override
+        public MemberName getKey() {
+            return definition.getName(index);
+        }
+
+        /** Returns the current record member value. */
+        @Override
+        public Object getValue() {
+            return locate(getKey());
+        }
+
+        /** Sets the record member value and returns the previous value. */
+        @Override
+        public Object setValue(final Object value) {
+            final MemberName name = getKey();
+            final Object previous = locate(name);
+            set(name, value);
+            return previous;
+        }
+    }
+
+    /**
+     * Returns the value for an attribute of the specified name.
+     *
+     * @param name The name of the attribute to lookup.
+     * @return The value of the attribute for the given name.
+     */
+    @Override
+    public Object locate(final MemberName name) {
+        final Integer index = definition.indexOf(name);
+        return (index != null) ? values[index] : null;
+    }
+
+    /**
+     * Sets the value for the attribute of the specified name.
+     *
+     * @param  name  The name of the attribute to modify.
+     * @param  value The new value for the attribute.
+     * @throws IllegalArgumentException if the given name is not a member of this record.
+     * @throws ClassCastException if the given value is not an instance of the expected type for this record.
+     */
+    @Override
+    public void set(final MemberName name, final Object value) {
+        final Integer index = definition.indexOf(name);
+        if (index == null) {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.PropertyNotFound_2,
+                    getRecordType().getTypeName(), name));
+        }
+        if (value != null) {
+            final Class<?> valueClass = definition.getValueClass(index);
+            if (valueClass != null && !valueClass.isInstance(value)) {
+                throw new ClassCastException(Errors.format(Errors.Keys.IllegalPropertyClass_2,
+                        name, value.getClass()));
+            }
+        }
+        values[index] = value;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecord.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordSchema.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -62,6 +62,14 @@ import org.apache.sis.internal.converter
  * on the part of the caller if the {@link NameFactory} given to the constructor is also thread-safe.
  * Subclasses should make sure that any overridden methods remain safe to call from multiple threads.
  *
+ * {@section Limitations}
+ * This class is currently not serializable because {@code RecordSchema} contain an arbitrary amount of record
+ * types in its {@linkplain #getDescription() description} map. Since each {@code RecordType} has a reference
+ * to its schema, serializing a single {@code RecordType} could imply serializing all of them.
+ * In order to reduce the risk of unexpected behavior, serialization is currently left to subclasses.
+ * For example a subclass may define a {@code Object readResolve()} method (as documented in the
+ * {@link java.io.Serializable} interface) returning a system-wide static constant for their schema.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.5
  * @version 0.5
@@ -75,11 +83,6 @@ public class DefaultRecordSchema impleme
     protected final NameFactory nameFactory;
 
     /**
-     * The {@linkplain #getSchemaName() schema name}.
-     */
-    private final LocalName schemaName;
-
-    /**
      * The namespace of {@link RecordType} to be created by this class.
      * This is also (indirectly) the {@linkplain #getSchemaName() schema name}.
      */
@@ -120,8 +123,7 @@ public class DefaultRecordSchema impleme
             nameFactory = DefaultFactories.NAMES;
         }
         this.nameFactory    = nameFactory;
-        this.schemaName     = nameFactory.createLocalName(parent, schemaName);
-        this.namespace      = nameFactory.createNameSpace(this.schemaName, null);
+        this.namespace      = nameFactory.createNameSpace(nameFactory.createLocalName(parent, schemaName), null);
         this.description    = new WeakValueHashMap<>(TypeName.class);
         this.attributeTypes = new HashMap<>();
     }
@@ -133,7 +135,7 @@ public class DefaultRecordSchema impleme
      */
     @Override
     public LocalName getSchemaName() {
-        return schemaName;
+        return namespace.name().tip();
     }
 
     /**
@@ -180,7 +182,7 @@ public class DefaultRecordSchema impleme
                 break; // Value classes differ.
             }
         }
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.RecordAlreadyDefined_2, schemaName, typeName));
+        throw new IllegalArgumentException(Errors.format(Errors.Keys.RecordAlreadyDefined_2, getSchemaName(), typeName));
     }
 
     /**
@@ -284,6 +286,6 @@ public class DefaultRecordSchema impleme
     @Debug
     @Override
     public String toString() {
-        return "RecordSchema[“" + schemaName + "”]";
+        return "RecordSchema[“" + getSchemaName() + "”]";
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultRecordType.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -18,9 +18,13 @@ package org.apache.sis.util.iso;
 
 import java.util.Set;
 import java.util.Map;
-import java.util.Collections;
 import java.util.LinkedHashMap;
+import java.util.Arrays;
 import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.InvalidObjectException;
 import javax.xml.bind.annotation.XmlType;
 import org.opengis.util.Type;
 import org.opengis.util.TypeName;
@@ -32,11 +36,11 @@ import org.opengis.util.NameFactory;
 import org.opengis.util.Record;
 import org.opengis.util.RecordType;
 import org.opengis.util.RecordSchema;
-import org.apache.sis.util.Debug;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.collection.Containers;
-import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.util.ObjectConverters;
+import org.apache.sis.internal.converter.SurjectiveConverter;
 
 // Branch-dependent imports
 import java.util.Objects;
@@ -56,11 +60,12 @@ import java.util.Objects;
  * {@section Instantiation}
  * The easiest way to create {@code DefaultRecordType} instances is to use the
  * {@link DefaultRecordSchema#createRecordType(CharSequence, Map)} method.
+ * Example:
  *
- * <div class="note"><b>Example:</b>
+ * <div class="note">
  * {@preformat java
  *     DefaultRecordSchema schema = new DefaultRecordSchema(null, null, "MySchema");
- *     // We recommand to reuse the same DefaultRecordSchema instance for all records to create in that schema.
+ *     // The same instance can be reused for all records to create in that schema.
  *
  *     Map<CharSequence,Class<?>> members = new LinkedHashMap<>();
  *     members.put("city",        String .class);
@@ -77,13 +82,18 @@ import java.util.Objects;
  * Subclasses shall make sure that any overridden methods remain safe to call from multiple threads and do not change
  * any public {@code RecordType} state.
  *
+ * {@section Serialization}
+ * This class is serializable if all elements given to the constructor are also serializable.
+ * Note in particular that {@link DefaultRecordSchema} is currently <strong>not</strong> serializable,
+ * so users wanting serialization may need to provide their own schema.
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @since   0.3
  * @version 0.5
  * @module
  */
 @XmlType(name = "RecordType")
-public class DefaultRecordType implements RecordType, Serializable {
+public class DefaultRecordType extends RecordDefinition implements RecordType, Serializable {
     /**
      * For cross-version compatibility.
      */
@@ -104,20 +114,18 @@ public class DefaultRecordType implement
     private final RecordSchema container;
 
     /**
-     * The dictionary of (<var>name</var>, <var>type</var>) pairs.
+     * The type of each members.
      *
-     * @see #getMembers()
      * @see #getMemberTypes()
      */
-    private final Map<MemberName,Type> memberTypes;
+    private transient Type[] memberTypes;
 
     /**
      * Empty constructor only used by JAXB.
      */
     private DefaultRecordType() {
-        typeName    = null;
-        container   = null;
-        memberTypes = Collections.emptyMap();
+        typeName  = null;
+        container = null;
     }
 
     /**
@@ -128,7 +136,7 @@ public class DefaultRecordType implement
     public DefaultRecordType(final RecordType other) {
         typeName    = other.getTypeName();
         container   = other.getContainer();
-        memberTypes = other.getMemberTypes();
+        memberTypes = computeTransientFields(other.getMemberTypes());
     }
 
     /**
@@ -141,18 +149,21 @@ public class DefaultRecordType implement
      * implementation is sufficient, the {@link DefaultRecordSchema#createRecordType(CharSequence, Map)}
      * method provides an easier alternative.</p>
      *
-     * @param typeName    The name that identifies this record type.
-     * @param container   The schema that contains this record type.
-     * @param memberTypes The name and type of the members to be included in this record type.
+     * @param typeName  The name that identifies this record type.
+     * @param container The schema that contains this record type.
+     * @param members   The name and type of the members to be included in this record type.
      *
      * @see DefaultRecordSchema#createRecordType(CharSequence, Map)
      */
     public DefaultRecordType(final TypeName typeName, final RecordSchema container,
-            final Map<? extends MemberName, ? extends Type> memberTypes)
+            final Map<? extends MemberName, ? extends Type> members)
     {
-        ArgumentChecks.ensureNonNull("typeName",    typeName);
-        ArgumentChecks.ensureNonNull("container",   container);
-        ArgumentChecks.ensureNonNull("memberTypes", memberTypes);
+        ArgumentChecks.ensureNonNull("typeName",  typeName);
+        ArgumentChecks.ensureNonNull("container", container);
+        ArgumentChecks.ensureNonNull("members",   members);
+        this.typeName    = typeName;
+        this.container   = container;
+        this.memberTypes = computeTransientFields(members);
         /*
          * Ensure that the record namespace is equals to the schema name. For example if the schema
          * name is "MyNameSpace", then the record type name can be "MyNameSpace:MyRecordType".
@@ -162,11 +173,10 @@ public class DefaultRecordType implement
         if (schemaName.compareTo(typeName.scope().name().tip()) != 0) {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.InconsistentNamespace_2, schemaName, fullTypeName));
         }
-        final Map<MemberName,Type> members = new LinkedHashMap<>(memberTypes);
-        members.remove(null);
-        for (final Map.Entry<MemberName,Type> entry : members.entrySet()) {
-            final MemberName name = entry.getKey();
-            final Type type = entry.getValue();
+        final int size = size();
+        for (int i=0; i<size; i++) {
+            final MemberName name = getName(i);
+            final Type type = this.memberTypes[i];
             if (type == null || name.getAttributeType().compareTo(type.getTypeName()) != 0) {
                 throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalMemberType_2, name, type));
             }
@@ -175,9 +185,6 @@ public class DefaultRecordType implement
                         fullTypeName, name.toFullyQualifiedName()));
             }
         }
-        this.typeName    = typeName;
-        this.container   = container;
-        this.memberTypes = CollectionsExt.unmodifiableOrCopy(members);
     }
 
     /**
@@ -192,6 +199,8 @@ public class DefaultRecordType implement
     DefaultRecordType(final TypeName typeName, final RecordSchema container,
             final Map<? extends CharSequence, ? extends Type> members, final NameFactory nameFactory)
     {
+        this.typeName  = typeName;
+        this.container = container;
         final NameSpace namespace = nameFactory.createNameSpace(typeName, null);
         final Map<MemberName,Type> memberTypes = new LinkedHashMap<>(Containers.hashMapCapacity(members.size()));
         for (final Map.Entry<? extends CharSequence, ? extends Type> entry : members.entrySet()) {
@@ -202,9 +211,41 @@ public class DefaultRecordType implement
                 throw new IllegalArgumentException(Errors.format(Errors.Keys.DuplicatedElement_1, member));
             }
         }
-        this.typeName    = typeName;
-        this.container   = container;
-        this.memberTypes = CollectionsExt.unmodifiableOrCopy(memberTypes);
+        this.memberTypes = computeTransientFields(memberTypes);
+    }
+
+    /**
+     * Invoked on deserialization for restoring the transient fields.
+     * See {@link #writeObject(ObjectOutputStream)} for the stream data description.
+     */
+    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+        in.defaultReadObject();
+        final int size = in.readInt();
+        final Map<MemberName,Type> members = new LinkedHashMap<>(Containers.hashMapCapacity(size));
+        for (int i=0; i<size; i++) {
+            final MemberName member = (MemberName) in.readObject();
+            final Type type = (Type) in.readObject();
+            if (members.put(member, type) != null) {
+                throw new InvalidObjectException(Errors.format(Errors.Keys.DuplicatedElement_1, member));
+            }
+        }
+        memberTypes = computeTransientFields(members);
+    }
+
+    /**
+     * Invoked on serialization for writing the member names and their type.
+     *
+     * @serialData The number of members as an {@code int}, followed by a
+     *             ({@code MemberName}, {@code Type}) pair for each member.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        final int size = size();
+        out.defaultWriteObject();
+        out.writeInt(size);
+        for (int i=0; i<size; i++) {
+            out.writeObject(getName(i));
+            out.writeObject(memberTypes[i]);
+        }
     }
 
     /**
@@ -234,6 +275,14 @@ public class DefaultRecordType implement
     }
 
     /**
+     * Returns {@code this} since {@link RecordDefinition} is the definition of this record type.
+     */
+    @Override
+    final RecordType getRecordType() {
+        return this;
+    }
+
+    /**
      * Returns the name that identifies this record type. If this {@code RecordType} is contained in a
      * {@linkplain DefaultRecordSchema record schema}, then the record type name shall be valid in the
      * {@linkplain DefaultNameSpace name space} of the record schema:
@@ -242,9 +291,10 @@ public class DefaultRecordType implement
      *     NameSpace namespace = getContainer().getSchemaName().scope()
      * }
      *
-     * {@section Comparison with Java reflection}
+     * <div class="note"><b>Comparison with Java reflection:</b>
      * If we think about this {@code RecordType} as equivalent to a {@code Class} instance,
      * then this method can be think as the equivalent of the Java {@link Class#getName()} method.
+     * </div>
      *
      * @return The name that identifies this record type.
      */
@@ -267,15 +317,20 @@ public class DefaultRecordType implement
      * Returns the dictionary of all (<var>name</var>, <var>type</var>) pairs in this record type.
      * The returned map is unmodifiable.
      *
-     * {@section Comparison with Java reflection}
+     * <div class="note"><b>Comparison with Java reflection:</b>
      * If we think about this {@code RecordType} as equivalent to a {@code Class} instance, then
      * this method can be though as the related to the Java {@link Class#getFields()} method.
+     * </div>
      *
      * @return The dictionary of (<var>name</var>, <var>type</var>) pairs, or an empty map if none.
      */
     @Override
-    public Map<MemberName, Type> getMemberTypes() {
-        return memberTypes;
+    public Map<MemberName,Type> getMemberTypes() {
+        return ObjectConverters.derivedValues(memberIndices(), MemberName.class, new SurjectiveConverter<Integer,Type>() {
+            @Override public Class<Integer> getSourceClass() {return Integer.class;}
+            @Override public Class<Type>    getTargetClass() {return Type.class;}
+            @Override public Type apply(final Integer index) {return getType(index);}
+        });
     }
 
     /**
@@ -290,7 +345,14 @@ public class DefaultRecordType implement
      */
     @Override
     public Set<MemberName> getMembers() {
-        return memberTypes.keySet();
+        return memberIndices().keySet();
+    }
+
+    /**
+     * Returns the type at the given index.
+     */
+    final Type getType(final int index) {
+        return memberTypes[index];
     }
 
     /**
@@ -301,17 +363,18 @@ public class DefaultRecordType implement
      *     getMemberTypes().get(memberName).getTypeName();
      * }
      *
-     * {@section Comparison with Java reflection}
+     * <div class="note"><b>Comparison with Java reflection:</b>
      * If we think about this {@code RecordType} as equivalent to a {@code Class} instance, then
      * this method can be though as related to the Java {@link Class#getField(String)} method.
+     * </div>
      *
      * @param  memberName The attribute name for which to get the associated type name.
      * @return The associated type name, or {@code null} if none.
      */
     @Override
     public TypeName locate(final MemberName memberName) {
-        final Type type = memberTypes.get(memberName);
-        return (type != null) ? type.getTypeName() : null;
+        final Integer index = indexOf(memberName);
+        return (index != null) ? getType(index).getTypeName() : null;
     }
 
     /**
@@ -348,9 +411,10 @@ public class DefaultRecordType implement
         }
         if (other != null && other.getClass() == getClass()) {
             final DefaultRecordType that = (DefaultRecordType) other;
-            return Objects.equals(typeName,    that.typeName)  &&
-                   Objects.equals(container,   that.container) &&
-                   Objects.equals(memberTypes, that.memberTypes);
+            return Objects.equals(typeName,    that.typeName)    &&
+                   Objects.equals(container,   that.container)   &&
+                   Arrays .equals(memberTypes, that.memberTypes) &&
+                   memberIndices().equals(that.memberIndices());
         }
         return false;
     }
@@ -360,19 +424,6 @@ public class DefaultRecordType implement
      */
     @Override
     public int hashCode() {
-        int code = memberTypes.hashCode();
-        if (typeName  != null) code = 31*code + typeName .hashCode();
-        if (container != null) code = 31*code + container.hashCode();
-        return code;
-    }
-
-    /**
-     * Returns a string representation of this {@code RecordType}.
-     * The string representation is for debugging purpose and may change in any future SIS version.
-     */
-    @Debug
-    @Override
-    public String toString() {
-        return "RecordType[\"" + typeName + "\"]";
+        return Objects.hashCode(typeName) + 31*(memberIndices().hashCode() + 31*Arrays.hashCode(memberTypes));
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultScopedName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultScopedName.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultScopedName.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/DefaultScopedName.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -183,7 +183,7 @@ public class DefaultScopedName extends A
         final LocalName lastName  = locals[index-1];
         final NameSpace lastScope = lastName.scope();
         final NameSpace tailScope = name.scope();
-        if (tailScope instanceof DefaultNameSpace && ((DefaultNameSpace) tailScope).parent == lastScope) {
+        if (tailScope instanceof DefaultNameSpace && ((DefaultNameSpace) tailScope).parent() == lastScope) {
             /*
              * If the tail is actually the tip (a LocalName), remember the tail so we
              * don't need to create it again later. Then copy the tail after the path.

Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java?rev=1608957&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -0,0 +1,220 @@
+/*
+ * 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.util.iso;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.io.Serializable;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.util.Type;
+import org.opengis.util.RecordType;
+import org.opengis.util.MemberName;
+import org.opengis.feature.AttributeType;
+import org.apache.sis.util.Debug;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.collection.Containers;
+import org.apache.sis.internal.util.CollectionsExt;
+
+
+/**
+ * Holds a {@code Record} definition in a way more convenient for Apache SIS than
+ * what the {@code RecordType} interface provides.
+ *
+ * {@section Serialization}
+ * This base class is intentionally not serializable, and all private fields are marked as transient for making
+ * this decision more visible. This is because the internal details of this class are quite arbitrary, so we do
+ * not want to expose them in serialization for compatibility reasons. Furthermore some information are redundant,
+ * so a serialization performed by subclasses may be more compact. Serialization of all necessary data shall be
+ * performed by subclasses, and the transient fields shall be reconstructed by a call to
+ * {@link #computeTransientFields(Map)}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+@XmlTransient
+abstract class RecordDefinition { // Intentionally not Serializable.
+    /**
+     * {@code RecordDefinition} implementation used as a fallback when the user-supplied {@link RecordType}
+     * is not an instance of {@link DefaultRecordType}. So this adapter is used only if Apache SIS is mixed
+     * with other implementations.
+     *
+     * {@section Serialization}
+     * This class is serializable if the {@code RecordType} given to the constructor is also serializable.
+     */
+    static final class Adapter extends RecordDefinition implements Serializable {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 3739362257927222288L;
+
+        /**
+         * The wrapped record type.
+         */
+        private final RecordType recordType; // This is the only serialized field in this file.
+
+        /**
+         * Creates a new adapter for the given record type.
+         */
+        Adapter(final RecordType recordType) {
+            this.recordType = recordType;
+            computeTransientFields(recordType.getMemberTypes());
+        }
+
+        /**
+         * Invoked on deserialization for restoring the transient fields.
+         */
+        private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+            in.defaultReadObject();
+            computeTransientFields(recordType.getMemberTypes());
+        }
+
+        /**
+         * Returns the wrapped record type.
+         */
+        @Override
+        RecordType getRecordType() {
+            return recordType;
+        }
+    }
+
+    /**
+     * Indices of member names. Created on construction or deserialization,
+     * and shall be considered final and unmodifiable after that point.
+     *
+     * @see #memberIndices()
+     */
+    private transient Map<MemberName,Integer> memberIndices;
+
+    /**
+     * Member names. Created on construction or deserialization, and shall be considered
+     * final and unmodifiable after that point.
+     */
+    private transient MemberName[] members;
+
+    /**
+     * Classes of expected values, or {@code null} if there is no restriction. Created on
+     * construction or deserialization, and shall be considered final and unmodifiable after that point.
+     */
+    private transient Class<?>[] valueClasses;
+
+    /**
+     * Creates a new instance. Subclasses shall invoke {@link #computeTransientFields(Map)} in their constructor.
+     */
+    RecordDefinition() {
+    }
+
+    /**
+     * Returns the record type for which this object is a definition.
+     */
+    abstract RecordType getRecordType();
+
+    /**
+     * Invoked on construction or deserialization for computing the transient fields.
+     *
+     * @param  memberTypes The (<var>name</var>, <var>type</var>) pairs in this record type.
+     * @return The values in the given map. This information is not stored in {@code RecordDefinition}
+     *         because not needed by this class, but the {@link DefaultRecordType} subclass will store it.
+     */
+    final Type[] computeTransientFields(final Map<? extends MemberName, ? extends Type> memberTypes) {
+        final int size = memberTypes.size();
+        members       = new MemberName[size];
+        memberIndices = new LinkedHashMap<>(Containers.hashMapCapacity(size));
+        final Type[] types = new Type[size];
+        int i = 0;
+        for (final Map.Entry<? extends MemberName, ? extends Type> entry : memberTypes.entrySet()) {
+            final Type type = entry.getValue();
+            if (type instanceof AttributeType) {
+                final Class<?> c = ((AttributeType) type).getValueClass();
+                if (c != Object.class) {
+                    if (valueClasses == null) {
+                        valueClasses = new Class<?>[size];
+                    }
+                    valueClasses[i] = c;
+                }
+            }
+            final MemberName name = entry.getKey();
+            members[i] = name;
+            memberIndices.put(name, i);
+            types[i] = type;
+            i++;
+        }
+        memberIndices = CollectionsExt.unmodifiableOrCopy(memberIndices);
+        return types;
+    }
+
+    /**
+     * Read-only access to the map of member indices.
+     */
+    final Map<MemberName,Integer> memberIndices() {
+        return memberIndices;
+    }
+
+    /**
+     * Returns the number of elements in records.
+     */
+    final int size() {
+        return members.length;
+    }
+
+    /**
+     * Returns the index of the given name, or {@code null} if none.
+     */
+    final Integer indexOf(final MemberName memberName) {
+        return memberIndices.get(memberName);
+    }
+
+    /**
+     * Returns the name of the member at the given index.
+     */
+    final MemberName getName(final int index) {
+        return members[index];
+    }
+
+    /**
+     * Returns the expected class of values in the given column, or {@code null} if unspecified.
+     */
+    final Class<?> getValueClass(final int index) {
+        return (valueClasses != null) ? valueClasses[index] : null;
+    }
+
+    /**
+     * Returns a string representation of this object.
+     * The string representation is for debugging purpose and may change in any future SIS version.
+     */
+    @Debug
+    @Override
+    public String toString() {
+        final StringBuilder buffer = new StringBuilder(250);
+        final String lineSeparator = System.lineSeparator();
+        final String[] names = new String[members.length];
+        int width = 0;
+        buffer.append("RecordType[“").append(getRecordType().getTypeName()).append("”] {").append(lineSeparator);
+        for (int i=0; i<names.length; i++) {
+            width = Math.max(width, (names[i] = members[i].toString()).length());
+        }
+        for (int i=0; i<names.length; i++) {
+            final String name = names[i];
+            buffer.append("    ").append(name).append(CharSequences.spaces(width - name.length()))
+                  .append(" : ").append(members[i].getAttributeType()).append(lineSeparator);
+        }
+        return buffer.append('}').append(lineSeparator).toString();
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/iso/RecordDefinition.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java?rev=1608957&r1=1608956&r2=1608957&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/DefaultRecordTypeTest.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -18,8 +18,10 @@ package org.apache.sis.util.iso;
 
 import java.util.Collections;
 import org.opengis.util.Type;
+import org.opengis.util.TypeName;
 import org.opengis.util.MemberName;
 import org.opengis.util.NameSpace;
+import org.opengis.util.RecordSchema;
 import org.apache.sis.internal.simple.SimpleAttributeType;
 
 // Test imports
@@ -28,7 +30,7 @@ import org.apache.sis.test.TestCase;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.DependsOnMethod;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 import static org.apache.sis.test.TestUtilities.getSingleton;
 
 
@@ -42,10 +44,10 @@ import static org.apache.sis.test.TestUt
  */
 @DependsOn(AbstractNameTest.class)
 public final strictfp class DefaultRecordTypeTest extends TestCase {
-    /** Value of {@link DefaultRecordType#getContainer()}.   */ private DefaultRecordSchema container;
-    /** Value of {@link DefaultRecordType#getTypeName()}.    */ private DefaultTypeName     recordTypeName;
-    /** Value of {@link DefaultRecordType#getMembers()}.     */ private DefaultMemberName   memberName;
-    /** Value of {@link DefaultRecordType#getMemberTypes()}. */ private DefaultTypeName     memberTypeName;
+    /** Value of {@link DefaultRecordType#getContainer()}.   */ private RecordSchema container;
+    /** Value of {@link DefaultRecordType#getTypeName()}.    */ private TypeName     recordTypeName;
+    /** Value of {@link DefaultRecordType#getMembers()}.     */ private MemberName   memberName;
+    /** Value of {@link DefaultRecordType#getMemberTypes()}. */ private TypeName     memberTypeName;
 
     /**
      * Initializes the private fields.
@@ -60,7 +62,7 @@ public final strictfp class DefaultRecor
         memberNamespace = new DefaultNameSpace (recordNamespace, "MyRecordType", ":", ":");
         memberTypeName  = new DefaultTypeName  (new DefaultNameSpace(null, "gco", ":", ":"), "Integer");
         memberName      = new DefaultMemberName(memberNamespace, "aMember", memberTypeName);
-        container       = new DefaultRecordSchema(null, null, "MyNameSpace");
+        container       = new SerializableRecordSchema("MyNameSpace");
         assertEquals("MyNameSpace:MyRecordType:aMember", memberName.toFullyQualifiedName().toString());
     }
 
@@ -97,7 +99,7 @@ public final strictfp class DefaultRecor
     @DependsOnMethod("testConstructor")
     public void testArgumentChecks() {
         init();
-        final DefaultTypeName  correctRecordName      = recordTypeName;
+        final TypeName         correctRecordName      = recordTypeName;
         final NameSpace        correctMemberNamespace = memberName.scope();
         final DefaultNameSpace wrongNamespace         = new DefaultNameSpace(null, "WrongNameSpace", ":", ":");
         /*
@@ -141,4 +143,21 @@ public final strictfp class DefaultRecor
             assertTrue(message, message.contains("gco:Integer"));
         }
     }
+
+    /**
+     * Tests serialization of a {@code RecordType}.
+     */
+    @Test
+    @DependsOnMethod("testConstructor")
+    public void testSerialization() {
+        init();
+        synchronized (SerializableRecordSchema.class) {
+            try {
+                SerializableRecordSchema.INSTANCE = container;
+                assertSerializedEquals(create());
+            } finally {
+                SerializableRecordSchema.INSTANCE = null;
+            }
+        }
+    }
 }

Added: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java?rev=1608957&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java [UTF-8] Tue Jul  8 21:40:57 2014
@@ -0,0 +1,65 @@
+/*
+ * 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.util.iso;
+
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import org.opengis.util.RecordSchema;
+
+
+/**
+ * A serializable {@link DefaultRecordSchema} for testing purpose only.
+ * On deserialization, the schema is replaced by the {@link #INSTANCE}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.5
+ * @version 0.5
+ * @module
+ */
+@SuppressWarnings("serial")
+final class SerializableRecordSchema extends DefaultRecordSchema implements Serializable {
+    /**
+     * The unique instance for the shema.
+     */
+    static RecordSchema INSTANCE;
+
+    /**
+     * Construct a new record schema.
+     *
+     * @param schemaName The name of the new schema.
+     */
+    SerializableRecordSchema(final String schemaName) {
+        super(null, null, schemaName);
+    }
+
+    /**
+     * On serialization, returns a proxy which will be resolved as {@link #INSTANCE} on deserialization.
+     */
+    Object writeReplace() throws ObjectStreamException {
+        return new Proxy();
+    }
+
+    /**
+     * The object to serialize instead of {@link DefaultRecordSchema}.
+     * This proxy is itself replaced by {@link SerializableRecordSchema#INSTANCE} on deserialization.
+     */
+    private static final class Proxy implements Serializable {
+        Object readResolve() throws ObjectStreamException {
+            return INSTANCE;
+        }
+    }
+}

Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/iso/SerializableRecordSchema.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message