]> git.aero2k.de Git - tmp/jakarta-migration.git/commitdiff
CSPACE-2699: Dates in the current format emitted by the UI date picker are now recogn...
authorAron Roberts <aron@socrates.berkeley.edu>
Tue, 24 Aug 2010 06:27:14 +0000 (06:27 +0000)
committerAron Roberts <aron@socrates.berkeley.edu>
Tue, 24 Aug 2010 06:27:14 +0000 (06:27 +0000)
services/common/src/main/config/services/tenant-bindings.xml
services/common/src/main/java/org/collectionspace/services/common/datetime/DateTimeFormatUtils.java
services/common/src/main/java/org/collectionspace/services/common/datetime/GregorianCalendarDateTimeUtils.java
services/common/src/main/java/org/collectionspace/services/common/document/DocumentUtils.java

index 0caf1ff9ffd87659c54e9b940ab421854f6316d8..1cb73143bed30f21658b30f616a6f579a4376fa8 100644 (file)
 
         <tenant:properties>
             <types:item><types:key>datePattern</types:key><types:value>MM/dd/yyyy</types:value></types:item>
+            <types:item><types:key>datePattern</types:key><types:value>MMM dd, yyyy</types:value></types:item>
             <types:item><types:key>datePattern</types:key><types:value>dd.MM.yyyy</types:value></types:item>
             <!-- <types:item><types:key>datePattern</types:key><types:value>dd/MM/yyyy</types:value></types:item> -->
-            <!-- <types:item><types:key>localeLanguage</types:key><types:value>en</types:value></types:item> -->
+            <types:item><types:key>localeLanguage</types:key><types:value>en</types:value></types:item>
             <!-- <types:item><types:key>localeLanguage</types:key><types:value>da</types:value></types:item> -->
         </tenant:properties>
 
index 289187d309d52cb073f941220515a00d50c2d3bf..9c3af532f1b8c8913b81196f5848c142c28e64f4 100644 (file)
@@ -6,7 +6,7 @@
  *  http://www.collectionspace.org
  *  http://wiki.collectionspace.org
 
- *  Copyright 2009 University of California at Berkeley
+ *  Copyright © 2009 University of California at Berkeley
 
  *  Licensed under the Educational Community License (ECL), Version 2.0.
  *  You may not use this file except in compliance with this License.
@@ -20,10 +20,12 @@ package org.collectionspace.services.common.datetime;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.GregorianCalendar;
-import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
@@ -49,10 +51,14 @@ public class DateTimeFormatUtils {
 
     private static final Logger logger = LoggerFactory.getLogger(DateTimeFormatUtils.class);
     final static String DATE_FORMAT_PATTERN_PROPERTY_NAME = "datePattern";
+    final static String LOCALE_LANGUAGE_CODE_PROPERTY_NAME = "localeLanguage";
+    final static Locale NULL_LOCALE = null;
+    final static List<String> isoLanguageCodes = new ArrayList(Arrays.asList(Locale.getISOLanguages()));
     final static String ISO_8601_FLOATING_DATE_PATTERN = "yyyy-MM-dd";
     final static String ISO_8601_UTC_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
     static Map<String,List<DateFormat>> dateFormatters = new HashMap<String,List<DateFormat>>();
     static Map<String,List<String>> datePatterns = new HashMap<String,List<String>>();
+    static Map<String,List<String>> localeLanguageCodes = new HashMap<String,List<String>>();
 
 
     // FIXME:
@@ -102,11 +108,31 @@ public class DateTimeFormatUtils {
         }
         // Otherwise, generate that list and cache it for re-use.
         List<String> patterns = getDateFormatPatternsForTenant(tenantId);
+        List<String> languageCodes = getLanguageCodesForTenant(tenantId);
+        Locale locale = null;
         DateFormat df = null;
-        for (String pattern : patterns) {
-            df = getDateFormatter(pattern);
-            if (df != null)  {
-                formatters.add(df);
+        boolean hasLanguageCodes = languageCodes != null && languageCodes.size() > 0;
+        // FIXME: this code pairs every locale language code with every date or
+        // date/time pattern.  This is a quick and dirty expedient, and must
+        // necessarily be replaced by date pattern/language code pairs.
+        if (hasLanguageCodes) {
+            for (String languageCode : languageCodes) {
+                if (languageCode != null && ! languageCode.trim().isEmpty()) {
+                    locale = getLocale(languageCode);
+                    for (String pattern : patterns) {
+                        df = getDateFormatter(pattern, locale);
+                        if (df != null)  {
+                            formatters.add(df);
+                        }
+                    }
+                }
+            }
+        } else {
+            for (String pattern : patterns) {
+                df = getDateFormatter(pattern, locale);
+                if (df != null)  {
+                    formatters.add(df);
+                }
             }
         }
         if (dateFormatters != null) {
@@ -198,10 +224,88 @@ public class DateTimeFormatUtils {
         return validPatterns;
     }
 
+    // FIXME: Routines specific to Locales, including their constituent language
+    // and country codes, should be moved to their own utility class, and likely
+    // to their own common package.
+
+    /**
+     * Returns a list of the locale language codes permitted in a service context.
+     *
+     * @param ctx a service context.
+     *
+     * @return    a list of locale language codes permitted in the service context.
+     *            Returns an empty list of language codes if the service context is null.
+     */
+    public static List<String> getLanguageCodesForTenant(ServiceContext ctx) {
+        if (ctx == null) {
+            return new ArrayList<String>();
+        }
+        return getLanguageCodesForTenant(ctx.getTenantId());
+    }
+
+    /**
+     * Returns a list of the locale language codes permitted for a tenant, specified
+     * by tenant ID.
+     *
+     * The values of these codes must be valid ISO 639-1 language codes.
+     *
+     * @param tenantId  a tenant ID.
+     *
+     * @return          a list of locale language codes permitted for the tenant.
+     *                  Returns an empty list of language codes if the tenant ID is null or empty.
+     */
+    public static List<String> getLanguageCodesForTenant(String tenantId) {
+        List<String> languageCodes = new ArrayList<String>();
+        if (tenantId == null || tenantId.trim().isEmpty()) {
+            return languageCodes;
+        }
+        // If a list of language codes for this tenant already exists, return it.
+        if (localeLanguageCodes != null && localeLanguageCodes.containsKey(tenantId)) {
+            languageCodes = localeLanguageCodes.get(tenantId);
+            if (languageCodes != null && languageCodes.size() > 0) {
+                return languageCodes;
+            }
+        }
+        // Otherwise, generate that list and cache it for re-use.
+        TenantBindingConfigReaderImpl tReader =
+                ServiceMain.getInstance().getTenantBindingConfigReader();
+        TenantBindingType tenantBinding = tReader.getTenantBinding(tenantId);
+        languageCodes = TenantBindingUtils.getPropertyValues(tenantBinding,
+                LOCALE_LANGUAGE_CODE_PROPERTY_NAME);
+        languageCodes = validateLanguageCodes(languageCodes);
+        if (localeLanguageCodes != null) {
+            localeLanguageCodes.put(tenantId, languageCodes);
+        }
+        return languageCodes;
+    }
+
+    /**
+     * Validates a list of language codes, verifying codes against a
+     * list of valid ISO 639-1 language codes.
+     *
+     * @param patterns  a list of language codes.
+     *
+     * @return          a list of valid language codes, excluding any codes
+     *                  that are not valid ISO 639-1 language codes.
+     */
+    public static List<String> validateLanguageCodes(List<String> languageCodes) {
+        if (languageCodes == null) {
+            return new ArrayList<String>();
+        }
+        List<String> validLanguageCodes = new ArrayList<String>();
+        for (String code : languageCodes) {
+            if (code != null && isoLanguageCodes.contains(code.trim())) {
+                validLanguageCodes.add(code);
+            }
+        }
+        return validLanguageCodes;
+    }
+
     /**
      * Returns an ISO 8601 timestamp representation of a presumptive date or
-     * date/time string.  Applies the set of date formatters for a supplied tenant
-     * to attempt to parse the string.
+     * date/time string.  Applies the set of date formatters for a supplied tenant,
+     * in sequence, to attempt to parse the string, and returns the timestamp
+     * resulting from the first successful parse attempt.
      *
      * @param str       a String, possibly a date or date/time String.
      * @param tenantId  a tenant ID.
@@ -244,7 +348,7 @@ public class DateTimeFormatUtils {
     }
 
     /**
-     * Formats a provided calendar date using a provided date formatter,
+     * Formats a provided calendar date using a supplied date formatter,
      * in the default system time zone.
      *
      * @param date  A calendar date to format.
@@ -372,6 +476,26 @@ public class DateTimeFormatUtils {
         return date;
     }
 
+    /**
+     * Returns the locale associated with a supplied ISO 639-1 language code.
+     *
+     * @param lang     A language code.
+     *
+     * @return         A locale based on that language code; or null
+     *                 if the code was null, empty, or invalid.
+     */
+    public static Locale getLocale(String lang) {
+        if (lang == null || lang.trim().isEmpty()) {
+            logger.warn("Null or empty date language code was provided when getting locale.");
+            return NULL_LOCALE;
+        }
+        if (! isoLanguageCodes.contains(lang.trim())) {
+            logger.warn("Invalid language code '" + lang + "'");
+            return NULL_LOCALE;
+        }
+        return new Locale(lang);
+    }
+
     /**
      * Returns a date formatter for a provided date or date/time pattern.
      *
@@ -381,13 +505,31 @@ public class DateTimeFormatUtils {
      *                 if the pattern was null, empty, or invalid.
      */
     public static DateFormat getDateFormatter(String pattern) {
+        return getDateFormatter(pattern, NULL_LOCALE);
+    }
+
+    /**
+     * Returns a date formatter for a supplied date or date/time pattern,
+     * in the supplied locale (if any).
+     *
+     * @param pattern  A date or date/time pattern.
+     * @param locale   A locale.
+     *
+     * @return         A date formatter using that pattern and locale (if any), or null
+     *                 if the pattern was null, empty, or invalid.
+     */
+    public static DateFormat getDateFormatter(String pattern, Locale locale) {
         DateFormat df = null;
         if (pattern == null || pattern.trim().isEmpty()) {
             logger.warn("Null or empty date pattern string was provided when getting date formatter.");
             return df;
         }
         try {
-            df = new SimpleDateFormat(pattern);
+            if (locale == null) {
+                df = new SimpleDateFormat(pattern);
+            } else {
+                df = new SimpleDateFormat(pattern, locale);
+            }
             df.setLenient(false);
         } catch (IllegalArgumentException iae) {
             logger.warn("Invalid date pattern string '" + pattern + "': " + iae.getMessage());
index 0aab30641f19d9cad82ff3a77d92a0828ea5f3b3..a37cc72359fe17bcd657441aec4b8f610ced6416 100644 (file)
@@ -6,7 +6,7 @@
  *  http://www.collectionspace.org
  *  http://wiki.collectionspace.org
 
- *  Copyright 2009 University of California at Berkeley
+ *  Copyright © 2009 University of California at Berkeley
 
  *  Licensed under the Educational Community License (ECL), Version 2.0.
  *  You may not use this file except in compliance with this License.
index 7d288ecf379434256544578d62fd1b9c082a2dcc..1e276a0aee1c4fe1056c08b43ea3e71f635c5380 100644 (file)
@@ -107,7 +107,7 @@ public class DocumentUtils {
        private static final Logger logger =
                LoggerFactory.getLogger(DocumentUtils.class);
 
-       /** The name value separator. */
+       /** The name dateVal separator. */
        private static String NAME_VALUE_SEPARATOR = "|";
 
        // The delimiter in a schema-qualified field name,
@@ -128,14 +128,14 @@ public class DocumentUtils {
         */
        private static class NameValue {        
                /**
-                * Instantiates a new name value.
+                * Instantiates a new name dateVal.
                 */
                NameValue() {
                        // default scoped constructor to removed "synthetic accessor" warning
                }        
                /** The name. */
                String name;        
-               /** The value. */
+               /** The dateVal. */
                String value;
        };
 
@@ -318,7 +318,7 @@ public class DocumentUtils {
        /**
         * parseProperties extract given payload (XML) into Name-Value properties. this
         * @param document to parse
-        * @return map key=property name, value=property value
+        * @return map key=property name, dateVal=property dateVal
         * @throws Exception
         */
        public static Map<String, Object> parseProperties(Node document)
@@ -346,8 +346,8 @@ public class DocumentUtils {
                                        }                    
                                }
                                //
-                               // Set the value even if it's null.
-                               // A null value implies a clear/delete of the property
+                               // Set the dateVal even if it's null.
+                               // A null dateVal implies a clear/delete of the property
                                //
                                objectProps.put(name, value);
                        }
@@ -356,7 +356,7 @@ public class DocumentUtils {
        }
 
        /**
-        * getMultiStringValues retrieve multi-value element values
+        * getMultiStringValues retrieve multi-dateVal element values
         * assumption: backend does not support more than 1 level deep hierarchy
         * @param node
         * @return
@@ -400,7 +400,7 @@ public class DocumentUtils {
        }
 
        /**
-        * getMultiValues retrieve multi-value element values
+        * getMultiValues retrieve multi-dateVal element values
         * assumption: backend does not support more than 1 level deep hierarchy
         * @param node
         * @return
@@ -437,7 +437,7 @@ public class DocumentUtils {
        }
 
        /**
-        * getTextNodeValue retrieves text node value
+        * getTextNodeValue retrieves text node dateVal
         * @param cnode
         * @return
         */
@@ -451,11 +451,11 @@ public class DocumentUtils {
        }
 
        /**
-        * isQualified check if the given value is already qualified with given property name
+        * isQualified check if the given dateVal is already qualified with given property name
         * e.g.  otherNumber|urn:org.collectionspace.id:24082390 is qualified with otherNumber
         * but urn:org.walkerart.id:123 is not qualified
         * @param name of the property, e.g. otherNumber
-        * @param value of the property e.g. otherNumber
+        * @param dateVal of the property e.g. otherNumber
         * @return
         */
        private static boolean isQualified(String name, String value) {
@@ -469,22 +469,22 @@ public class DocumentUtils {
        }
 
        /**
-        * qualify qualifies given property value with given property name, e.g.
-        * name=otherNumber and value=urn:org.collectionspace.id:24082390 would be
+        * qualify qualifies given property dateVal with given property name, e.g.
+        * name=otherNumber and dateVal=urn:org.collectionspace.id:24082390 would be
         * qualified as otherNumber|urn:org.collectionspace.id:24082390. however,
-        * name=otherNumber and value=otherNumber|urn:org.collectionspace.id:24082390
-        * would be ignored as the given value is already qualified once.
+        * name=otherNumber and dateVal=otherNumber|urn:org.collectionspace.id:24082390
+        * would be ignored as the given dateVal is already qualified once.
         * @param name
-        * @param value
+        * @param dateVal
         * @return
         */
        private static String qualify(String name, String value) {
                /*
         String result = null;
-        if (isQualified(name, value)) {
-            result = value;
+        if (isQualified(name, dateVal)) {
+            result = dateVal;
         } else {
-               result = name + NAME_VALUE_SEPARATOR + value;
+               result = name + NAME_VALUE_SEPARATOR + dateVal;
         }
         return result;
                 */
@@ -566,7 +566,7 @@ public class DocumentUtils {
         * @param document the document
         * @param parent the parent
         * @param field the field
-        * @param value the value
+        * @param dateVal the dateVal
         * @throws IOException Signals that an I/O exception has occurred.
         */
        private static void buildProperty(Document document, Element parent, 
@@ -866,7 +866,7 @@ public class DocumentUtils {
             NameValue nv = unqualify(val);
             Element c = document.createElement(nv.name);
             e.appendChild(c);
-            insertTextNode(document, c, nv.value);
+            insertTextNode(document, c, nv.dateVal);
         }
     }
         */
@@ -941,7 +941,7 @@ public class DocumentUtils {
         *
         * @param document the document
         * @param e the e
-        * @param strValue the str value
+        * @param strValue the str dateVal
     private static void insertTextNode(Document document, Element e, String strValue) {
         Text tNode = document.createTextNode(strValue);
         e.appendChild(tNode);
@@ -949,11 +949,11 @@ public class DocumentUtils {
         */
 
        /**
-        * unqualify given value.
+        * unqualify given dateVal.
         * input of otherNumber|urn:org.collectionspace.id:24082390 would be unqualified
-        * as name=otherNumber and value=urn:org.collectionspace.id:24082390
+        * as name=otherNumber and dateVal=urn:org.collectionspace.id:24082390
         * @param input
-        * @return name and value
+        * @return name and dateVal
         * @exception IllegalStateException
     private static NameValue unqualify(String input) {
         NameValue nv = new NameValue();
@@ -961,11 +961,11 @@ public class DocumentUtils {
         int tokens = stz.countTokens();
         if (tokens == 2) {
             nv.name = stz.nextToken();
-            nv.value = stz.nextToken();
+            nv.dateVal = stz.nextToken();
             // Allow null or empty values
         } else if (tokens == 1) {
             nv.name = stz.nextToken();
-            nv.value = "";
+            nv.dateVal = "";
         } else {
             throw new IllegalStateException("Unexpected format for multi valued element: " + input);
         }
@@ -1151,23 +1151,23 @@ public class DocumentUtils {
                
                if (type.isSimpleType()) {
                         if (isDateType(type)) {
-                            String value = element.getText();
-                            if (value == null || value.trim().isEmpty()) {
+                            String dateVal = element.getText();
+                            if (dateVal == null || dateVal.trim().isEmpty()) {
                                 result = type.decode("");
                             } else {
-                                // Dates or date/times in ISO 8601-based representations
+                                // Dates or date/times in any ISO 8601-based representations
                                 // directly supported by Nuxeo will be successfully decoded.
-                                result = type.decode(element.getText());
+                                result = type.decode(dateVal);
                                 // All other date or date/time values must first be converted
                                 // to a supported ISO 8601-based representation.
                                 if (result == null) {
-                                    dateStr = DateTimeFormatUtils.toIso8601Timestamp(element.getText(),
+                                    dateStr = DateTimeFormatUtils.toIso8601Timestamp(dateVal,
                                             ctx.getTenantId());
                                     if (dateStr != null) {
                                         result = type.decode(dateStr);
                                     } else {
                                         throw new IllegalArgumentException("Unrecognized date value '"
-                                                + element.getText() + "' in field '" + element.getName() + "'");
+                                                + dateVal + "' in field '" + element.getName() + "'");
                                     }
                                 }
                             }