sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1462033 - in /sis/branches/JDK7/sis-metadata/src: main/java/org/apache/sis/metadata/PropertyAccessor.java main/java/org/apache/sis/metadata/PropertyComparator.java test/java/org/apache/sis/metadata/PropertyAccessorTest.java
Date Thu, 28 Mar 2013 11:04:15 GMT
Author: desruisseaux
Date: Thu Mar 28 11:04:14 2013
New Revision: 1462033

URL: http://svn.apache.org/r1462033
Log:
PropertyAccessor shall uses the ordering defined by XmlType.propOrder annotation.

Modified:
    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/PropertyComparator.java
    sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java

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=1462033&r1=1462032&r2=1462033&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] Thu Mar 28 11:04:14 2013
@@ -49,6 +49,7 @@ import org.apache.sis.xml.IdentifiedObje
 import static org.apache.sis.util.collection.CollectionsExt.modifiableCopy;
 import static org.apache.sis.util.collection.CollectionsExt.hashMapCapacity;
 import static org.apache.sis.internal.util.Utilities.floatEpsilonEqual;
+import static org.apache.sis.metadata.PropertyComparator.*;
 
 
 /**
@@ -79,21 +80,6 @@ import static org.apache.sis.internal.ut
 @ThreadSafe
 final class PropertyAccessor {
     /**
-     * The prefix for getters on boolean values.
-     */
-    private static final String IS = "is";
-
-    /**
-     * The prefix for getters (general case).
-     */
-    private static final String GET = "get";
-
-    /**
-     * The prefix for setters.
-     */
-    private static final String SET = "set";
-
-    /**
      * Getters shared between many instances of this class. Two different implementations
      * may share the same getters but different setters.
      *
@@ -236,7 +222,7 @@ final class PropertyAccessor {
         this.standard       = standard;
         this.type           = type;
         this.implementation = implementation;
-        this.getters        = getGetters(type);
+        this.getters        = getGetters(type, implementation);
         int allCount = getters.length;
         int standardCount = allCount;
         if (allCount != 0 && getters[allCount-1] == EXTRA_GETTER) {
@@ -375,9 +361,10 @@ final class PropertyAccessor {
      * since it may be shared among many instances of {@code PropertyAccessor}.
      *
      * @param  type The metadata interface.
+     * @param  implementation The class of metadata implementations.
      * @return The getters declared in the given interface (never {@code null}).
      */
-    private static Method[] getGetters(final Class<?> type) {
+    private static Method[] getGetters(final Class<?> type, final Class<?> implementation)
{
         synchronized (SHARED_GETTERS) {
             Method[] getters = SHARED_GETTERS.get(type);
             if (getters == null) {
@@ -425,7 +412,7 @@ final class PropertyAccessor {
                  * keep the extra methods last. The code checking for the extra methods require
                  * them to be last.
                  */
-                Arrays.sort(getters, 0, count, PropertyComparator.INSTANCE);
+                Arrays.sort(getters, 0, count, new PropertyComparator(implementation));
                 if (!hasExtraGetter) {
                     if (getters.length == count) {
                         getters = Arrays.copyOf(getters, count+1);
@@ -440,70 +427,6 @@ final class PropertyAccessor {
     }
 
     /**
-     * Returns the prefix of the specified method name. If the method name doesn't starts
with
-     * a prefix (for example {@link org.opengis.metadata.quality.ConformanceResult#pass()}),
-     * then this method returns an empty string.
-     */
-    private static String prefix(final String name) {
-        if (name.startsWith(GET)) {
-            return GET;
-        }
-        if (name.startsWith(IS)) {
-            return IS;
-        }
-        if (name.startsWith(SET)) {
-            return SET;
-        }
-        return "";
-    }
-
-    /**
-     * Returns {@code true} if the specified string starting at the specified index contains
-     * no lower case characters. The characters don't have to be in upper case however (e.g.
-     * non-alphabetic characters)
-     */
-    private static boolean isAcronym(final String name, int offset) {
-        final int length = name.length();
-        while (offset < length) {
-            final int c = name.codePointAt(offset);
-            if (Character.isLowerCase(c)) {
-                return false;
-            }
-            offset += Character.charCount(c);
-        }
-        return true;
-    }
-
-    /**
-     * Removes the {@code "get"} or {@code "is"} prefix and turn the first character after
the
-     * prefix into lower case. For example the method name {@code "getTitle"} will be replaced
-     * by the property name {@code "title"}. We will perform this operation only if there
is
-     * at least 1 character after the prefix.
-     *
-     * @param  name The method name (can not be {@code null}).
-     * @param  base Must be the result of {@code prefix(name).length()}.
-     * @return The property name (never {@code null}).
-     */
-    private static String toPropertyName(String name, final int base) {
-        final int length = name.length();
-        if (length > base) {
-            if (isAcronym(name, base)) {
-                name = name.substring(base);
-            } else {
-                final int up = name.codePointAt(base);
-                final int lo = Character.toLowerCase(up);
-                if (up != lo) {
-                    name = new StringBuilder(length - base).appendCodePoint(lo)
-                            .append(name, base + Character.charCount(up), length).toString();
-                } else {
-                    name = name.substring(base);
-                }
-            }
-        }
-        return name.intern();
-    }
-
-    /**
      * Returns the number of properties that can be read.
      */
     final int count() {

Modified: sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyComparator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyComparator.java?rev=1462033&r1=1462032&r2=1462033&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyComparator.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyComparator.java
[UTF-8] Thu Mar 28 11:04:14 2013
@@ -17,33 +17,78 @@
 package org.apache.sis.metadata;
 
 import java.util.Comparator;
+import java.util.Map;
+import java.util.IdentityHashMap;
 import java.lang.reflect.Method;
-import net.jcip.annotations.Immutable;
+import javax.xml.bind.annotation.XmlType;
 
 import org.opengis.annotation.UML;
 import org.opengis.annotation.Obligation;
 
 
 /**
- * The comparator for sorting method order. This comparator puts mandatory methods first,
- * which is necessary for reducing the risk of ambiguity in {@link MetadataTreeFormat#parse}.
+ * The comparator for sorting the properties in a metadata object.
+ * Since the comparator uses (among other criterion) the property names, this class
+ * incidentally defines static methods for inferring those names from the methods.
+ *
+ * <p>This comparator uses the following criterion, in priority order:</p>
+ * <ol>
+ *   <li>If the property order is specified by a {@link XmlType} annotation,
+ *       then this comparator complies to that order.</li>
+ *   <li>Otherwise this comparator sorts mandatory methods first, followed by
+ *       conditional methods, then optional ones.</li>
+ *   <li>If the order can not be inferred from the above, then the comparator
+ *       fallbacks on alphabetical order.</li>
+ * </ol>
+ *
+ * The first criterion (mandatory methods first) is necessary for reducing the risk
+ * of ambiguity in the {@link MetadataTreeTable#parse} method.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.4)
  * @version 0.3
  * @module
  */
-@Immutable
 final class PropertyComparator implements Comparator<Method> {
     /**
-     * The singleton instance.
+     * The prefix for getters on boolean values.
+     */
+    private static final String IS = "is";
+
+    /**
+     * The prefix for getters (general case).
+     */
+    private static final String GET = "get";
+
+    /**
+     * The prefix for setters.
+     */
+    static final String SET = "set";
+
+    /**
+     * Methods specified in the {@link XmlType} annotation, or {@code null} if none.
      */
-    static final Comparator<Method> INSTANCE = new PropertyComparator();
+    private final String[] order;
 
     /**
-     * Do not allow instantiation of this class, except for the singleton.
+     * Indices of methods in the {@link #order} array, created when first needed.
      */
-    private PropertyComparator() {
+    private Map<Method,Integer> indices;
+
+    /**
+     * Creates a new comparator for the given implementation class.
+     *
+     * @param implementation The implementation class, or {@code null} if unknown.
+     */
+    PropertyComparator(final Class<?> implementation) {
+        if (implementation != null) {
+            final XmlType xml = implementation.getAnnotation(XmlType.class);
+            if (xml != null) {
+                order = xml.propOrder();
+                return;
+            }
+        }
+        order = null;
     }
 
     /**
@@ -51,27 +96,31 @@ final class PropertyComparator implement
      */
     @Override
     public int compare(final Method m1, final Method m2) {
-        final UML a1 = m1.getAnnotation(UML.class);
-        final UML a2 = m2.getAnnotation(UML.class);
-        if (a1 != null) {
-            if (a2 == null) return +1;       // Sort annotated elements first.
-            int c = order(a1) - order(a2);   // Mandatory elements must be first.
-            if (c == 0) {
-                // Fallback on alphabetical order.
-                c = a1.identifier().compareToIgnoreCase(a2.identifier());
-            }
-            return c;
-        } else if (a2 != null) {
-            return -1; // Sort annotated elements first.
+        int c = indexOf(m1) - indexOf(m2);
+        if (c == 0) {
+            final UML a1 = m1.getAnnotation(UML.class);
+            final UML a2 = m2.getAnnotation(UML.class);
+            if (a1 != null) {
+                if (a2 == null) return +1;   // Sort annotated elements first.
+                c = order(a1) - order(a2);   // Mandatory elements must be first.
+                if (c == 0) {
+                    // Fallback on alphabetical order.
+                    c = a1.identifier().compareToIgnoreCase(a2.identifier());
+                }
+                return c;
+            } else if (a2 != null) {
+                return -1; // Sort annotated elements first.
+            }
+            // Fallback on alphabetical order.
+            c = m1.getName().compareToIgnoreCase(m2.getName());
         }
-        // Fallback on alphabetical order.
-        return m1.getName().compareToIgnoreCase(m2.getName());
+        return c;
     }
 
     /**
      * Returns a higher number for obligation which should be first.
      */
-    private int order(final UML uml) {
+    private static int order(final UML uml) {
         final Obligation obligation = uml.obligation();
         if (obligation != null) {
             switch (obligation) {
@@ -83,4 +132,95 @@ final class PropertyComparator implement
         }
         return 5;
     }
+
+    /**
+     * Returns the index of the given method, or {@code order.length} if the method is not
found.
+     */
+    private int indexOf(final Method method) {
+        int i = 0;
+        if (order != null) {
+            if (indices == null) {
+                indices = new IdentityHashMap<>();
+            } else {
+                Integer index = indices.get(method);
+                if (index != null) {
+                    return index;
+                }
+            }
+            String name = method.getName();
+            name = toPropertyName(name, prefix(name).length());
+            while (i < order.length) {
+                if (name.equals(order[i])) {
+                    break;
+                }
+                i++;
+            }
+            indices.put(method, i);
+        }
+        return i;
+    }
+
+    /**
+     * Returns the prefix of the specified method name. If the method name doesn't starts
with
+     * a prefix (for example {@link org.opengis.metadata.quality.ConformanceResult#pass()}),
+     * then this method returns an empty string.
+     */
+    static String prefix(final String name) {
+        if (name.startsWith(GET)) {
+            return GET;
+        }
+        if (name.startsWith(IS)) {
+            return IS;
+        }
+        if (name.startsWith(SET)) {
+            return SET;
+        }
+        return "";
+    }
+
+    /**
+     * Returns {@code true} if the specified string starting at the specified index contains
+     * no lower case characters. The characters don't have to be in upper case however (e.g.
+     * non-alphabetic characters)
+     */
+    private static boolean isAcronym(final String name, int offset) {
+        final int length = name.length();
+        while (offset < length) {
+            final int c = name.codePointAt(offset);
+            if (Character.isLowerCase(c)) {
+                return false;
+            }
+            offset += Character.charCount(c);
+        }
+        return true;
+    }
+
+    /**
+     * Removes the {@code "get"} or {@code "is"} prefix and turn the first character after
the
+     * prefix into lower case. For example the method name {@code "getTitle"} will be replaced
+     * by the property name {@code "title"}. We will perform this operation only if there
is
+     * at least 1 character after the prefix.
+     *
+     * @param  name The method name (can not be {@code null}).
+     * @param  base Must be the result of {@code prefix(name).length()}.
+     * @return The property name (never {@code null}).
+     */
+    static String toPropertyName(String name, final int base) {
+        final int length = name.length();
+        if (length > base) {
+            if (isAcronym(name, base)) {
+                name = name.substring(base);
+            } else {
+                final int up = name.codePointAt(base);
+                final int lo = Character.toLowerCase(up);
+                if (up != lo) {
+                    name = new StringBuilder(length - base).appendCodePoint(lo)
+                            .append(name, base + Character.charCount(up), length).toString();
+                } else {
+                    name = name.substring(base);
+                }
+            }
+        }
+        return name.intern();
+    }
 }

Modified: sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java?rev=1462033&r1=1462032&r2=1462033&view=diff
==============================================================================
--- sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/sis-metadata/src/test/java/org/apache/sis/metadata/PropertyAccessorTest.java
[UTF-8] Thu Mar 28 11:04:14 2013
@@ -140,26 +140,27 @@ public final strictfp class PropertyAcce
     }
 
     /**
-     * Tests the constructor. This test may need to be updated if a future GeoAPI release
-     * modifies the {@link Citation} interface.
+     * Tests the constructor with the {@link DefaultCitation} implementation.
+     * The order of properties shall be the order declared in the {@code XmlType.propOrder}
annotation.
+     * This test may need to be updated if a future GeoAPI release modifies the {@link Citation}
interface.
      */
     @Test
     public void testConstructor() {
         assertMappingEquals(createPropertyAccessor(),
         //……Declaring type………Method………………………………………………………………JavaBeans………………………………………………UML
identifier……………………………Sentence………………………………………………………Type………………………………………………………………
-/*Required*/Citation.class, "getDates",                   "dates",                   "date",
                 "Dates",                      CitationDate[].class,
             Citation.class, "getTitle",                   "title",                   "title",
                "Title",                      InternationalString.class,
-/*Optional*/Citation.class, "getAlternateTitles",         "alternateTitles",         "alternateTitle",
       "Alternate titles",           InternationalString[].class,
-            Citation.class, "getCitedResponsibleParties", "citedResponsibleParties", "citedResponsibleParty",
"Cited responsible parties",  ResponsibleParty[].class,
-            Citation.class, "getCollectiveTitle",         "collectiveTitle",         "collectiveTitle",
      "Collective title",           InternationalString.class,
+            Citation.class, "getAlternateTitles",         "alternateTitles",         "alternateTitle",
       "Alternate titles",           InternationalString[].class,
+            Citation.class, "getDates",                   "dates",                   "date",
                 "Dates",                      CitationDate[].class,
             Citation.class, "getEdition",                 "edition",                 "edition",
              "Edition",                    InternationalString.class,
             Citation.class, "getEditionDate",             "editionDate",             "editionDate",
          "Edition date",               Date.class,
             Citation.class, "getIdentifiers",             "identifiers",             "identifier",
           "Identifiers",                Identifier[].class,
-            Citation.class, "getISBN",                    "ISBN",                    "ISBN",
                 "ISBN",                       String.class,
-            Citation.class, "getISSN",                    "ISSN",                    "ISSN",
                 "ISSN",                       String.class,
-            Citation.class, "getOtherCitationDetails",    "otherCitationDetails",    "otherCitationDetails",
 "Other citation details",     InternationalString.class,
+            Citation.class, "getCitedResponsibleParties", "citedResponsibleParties", "citedResponsibleParty",
"Cited responsible parties",  ResponsibleParty[].class,
             Citation.class, "getPresentationForms",       "presentationForms",       "presentationForm",
     "Presentation forms",         PresentationForm[].class,
-            Citation.class, "getSeries",                  "series",                  "series",
               "Series",                     Series.class);
+            Citation.class, "getSeries",                  "series",                  "series",
               "Series",                     Series.class,
+            Citation.class, "getOtherCitationDetails",    "otherCitationDetails",    "otherCitationDetails",
 "Other citation details",     InternationalString.class,
+            Citation.class, "getCollectiveTitle",         "collectiveTitle",         "collectiveTitle",
      "Collective title",           InternationalString.class,
+            Citation.class, "getISBN",                    "ISBN",                    "ISBN",
                 "ISBN",                       String.class,
+            Citation.class, "getISSN",                    "ISSN",                    "ISSN",
                 "ISSN",                       String.class);
     }
 
     /**



Mime
View raw message