You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2013/05/17 22:57:06 UTC

svn commit: r1483990 - in /sis/branches/JDK7: core/sis-utility/src/main/java/org/apache/sis/internal/util/ core/sis-utility/src/test/java/org/apache/sis/internal/util/ core/sis-utility/src/test/java/org/apache/sis/test/suite/ ide-project/NetBeans/nbpro...

Author: desruisseaux
Date: Fri May 17 20:57:05 2013
New Revision: 1483990

URL: http://svn.apache.org/r1483990
Log:
Added support for date/time units in ChannelDecoder.

Added:
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/UtilitiesTest.java   (with props)
Modified:
    sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java
    sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
    sis/branches/JDK7/ide-project/NetBeans/nbproject/project.properties
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
    sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java
    sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java

Modified: sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java?rev=1483990&r1=1483989&r2=1483990&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java [UTF-8] Fri May 17 20:57:05 2013
@@ -16,8 +16,10 @@
  */
 package org.apache.sis.internal.util;
 
+import java.util.Date;
 import java.util.Formatter;
 import java.util.FormattableFlags;
+import javax.xml.bind.DatatypeConverter;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.CharSequences;
 
@@ -117,6 +119,103 @@ public final class Utilities extends Sta
     }
 
     /**
+     * Parses a date from a string in ISO 8601 format. More specifically, this method expects the
+     * format defined by <cite>XML Schema Part 2: Datatypes for {@code xsd:dateTime}</cite>, with
+     * some additional flexibility (e.g. missing minutes or seconds fields are automatically added).
+     *
+     * @param  date The date to parse, or {@code null}.
+     * @param  defaultToUTC {@code true} if the default timezone shall be UTL, or {@code false} for locale default.
+     * @return The parsed date, or {@code null} if the given string was {@code null}.
+     * @throws IllegalArgumentException if the given date can not be parsed.
+     *
+     * @see DatatypeConverter#parseDateTime(String)
+     */
+    public static Date parseDateTime(String date, final boolean defaultToUTC) throws IllegalArgumentException {
+        if (date != null) {
+            date = CharSequences.trimWhitespaces(date);
+            if (!date.isEmpty()) {
+                /*
+                 * Check for missing time fields and time zone. For example if the given date is
+                 * "2005-09-22T00:00", then this block will complete it as "2005-09-22T00:00:00".
+                 * In addition, a 'Z' suffix will be appended if 'defaultToUTC' is true.
+                 */
+                int timeFieldStart  = date.lastIndexOf('T') + 1; // 0 if there is no time field.
+                int timeFieldEnd    = date.length();             // To be updated if there is a time field.
+                int missingFields   = 0;                         // Number of missing time fields.
+                boolean hasTimeZone = date.charAt(timeFieldEnd - 1) == 'Z';
+                if (timeFieldStart != 0) {
+                    if (hasTimeZone) {
+                        timeFieldEnd--;
+                    } else {
+                        final int s = Math.max(date.indexOf('+', timeFieldStart),
+                                               date.indexOf('-', timeFieldStart));
+                        hasTimeZone = (s >= 0);
+                        if (hasTimeZone) {
+                            timeFieldEnd = s;
+                        }
+                    }
+                    missingFields = 2;
+                    for (int i=timeFieldStart; i<timeFieldEnd; i++) {
+                        if (date.charAt(i) == ':') {
+                            if (--missingFields == 0) break;
+                        }
+                    }
+                }
+                /*
+                 * At this point, we have determined if there is some missing fields.
+                 * The timezone will be considered missing only if 'defaultToUTC' is true.
+                 */
+                CharSequence modified = date;
+                hasTimeZone |= !defaultToUTC;
+                if (missingFields != 0 || !hasTimeZone) {
+                    final StringBuilder buffer = new StringBuilder(date);
+                    while (--missingFields >= 0) {
+                        buffer.append(":00");
+                    }
+                    if (!hasTimeZone) {
+                        buffer.append('Z');
+                    }
+                    modified = buffer;
+                }
+                /*
+                 * Now ensure that all numbers have at least two digits.
+                 */
+                int indexOfLastDigit = 0;
+                for (int i=modified.length(); --i >= 0;) {
+                    char c = modified.charAt(i);
+                    final boolean isDigit = (c >= '0' && c <= '9'); // Do not use Character.isDigit(char).
+                    if (indexOfLastDigit == 0) {
+                        // We were not scaning a number. Check if we are now starting doing so.
+                        if (isDigit) {
+                            indexOfLastDigit = i;
+                        }
+                    } else {
+                        // We were scaning a number. Check if we found the begining.
+                        if (!isDigit) {
+                            if (indexOfLastDigit - i == 1) {
+                                // Reuse the buffer if it exists, or create a new one otherwise.
+                                final StringBuilder buffer;
+                                if (modified == date) {
+                                    modified = buffer = new StringBuilder(date);
+                                } else {
+                                    buffer = (StringBuilder) modified;
+                                }
+                                buffer.insert(i+1, '0');
+                            }
+                            indexOfLastDigit = 0;
+                        }
+                    }
+                }
+                /*
+                 * Now delegate to the utility method provided in the JAXB packages.
+                 */
+                return DatatypeConverter.parseDateTime(modified.toString()).getTime();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Formats the given character sequence to the given formatter. This method takes in account
      * the {@link FormattableFlags#UPPERCASE} and {@link FormattableFlags#LEFT_JUSTIFY} flags.
      *

Added: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/UtilitiesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/UtilitiesTest.java?rev=1483990&view=auto
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/UtilitiesTest.java (added)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/UtilitiesTest.java [UTF-8] Fri May 17 20:57:05 2013
@@ -0,0 +1,61 @@
+/*
+ * 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.internal.util;
+
+import java.util.Date;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+import static org.apache.sis.test.TestUtilities.date;
+
+
+/**
+ * Tests the {@link Utilities} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+public final strictfp class UtilitiesTest extends TestCase {
+    /**
+     * Tests the {@link Utilities#parseDateTime(String)} method with an explicit time zone.
+     */
+    @Test
+    public void testParseDateTime() {
+        final Date expected = date("2009-01-01 05:00:00");
+        assertEquals(expected, Utilities.parseDateTime("2009-01-01T06:00:00+01:00", false));
+        assertEquals(expected, Utilities.parseDateTime("2009-01-01T06:00:00+01:00", true));
+    }
+
+    /**
+     * Tests the {@link Utilities#parseDateTime(String)} method for a date in UTC time zone,
+     * either explicit or implicit.
+     */
+    @Test
+    public void testParseDateTimeUTC() {
+        final Date expected = date("2005-09-22 00:00:00");
+        assertEquals(expected, Utilities.parseDateTime("2005-09-22T00:00:00Z", false));
+        assertEquals(expected, Utilities.parseDateTime("2005-09-22T00:00:00Z", true));
+        assertEquals(expected, Utilities.parseDateTime("2005-09-22T00:00:00",  true));
+        assertEquals(expected, Utilities.parseDateTime("2005-09-22T00:00",     true));
+        assertEquals(expected, Utilities.parseDateTime("2005-09-22T00",        true));
+        assertEquals(expected, Utilities.parseDateTime("2005-09-22",           true));
+        assertEquals(expected, Utilities.parseDateTime("2005-9-22",            true));
+    }
+}

Propchange: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/internal/util/UtilitiesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

Modified: sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1483990&r1=1483989&r2=1483990&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Fri May 17 20:57:05 2013
@@ -50,6 +50,7 @@ import org.junit.BeforeClass;
     org.apache.sis.math.MathFunctionsTest.class,
     org.apache.sis.math.StatisticsTest.class,
     org.apache.sis.math.StatisticsFormatTest.class,
+    org.apache.sis.internal.util.UtilitiesTest.class,
 
     // Collections.
     org.apache.sis.internal.util.ReferenceQueueConsumerTest.class,

Modified: sis/branches/JDK7/ide-project/NetBeans/nbproject/project.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/ide-project/NetBeans/nbproject/project.properties?rev=1483990&r1=1483989&r2=1483990&view=diff
==============================================================================
--- sis/branches/JDK7/ide-project/NetBeans/nbproject/project.properties [ISO-8859-1] (original)
+++ sis/branches/JDK7/ide-project/NetBeans/nbproject/project.properties [ISO-8859-1] Fri May 17 20:57:05 2013
@@ -92,6 +92,7 @@ run.classpath=\
 run.test.classpath=\
     ${javac.test.classpath}:\
     ${build.test.classes.dir}:\
+    ${maven.repository}/edu/ucar/udunits/${netcdf.version}/udunits-${netcdf.version}.jar:\
     ${maven.repository}/joda-time/joda-time/${joda-time.version}/joda-time-${joda-time.version}.jar:\
     ${maven.repository}/org/slf4j/slf4j-api/${slf4j.version}/slf4j-api-${slf4j.version}.jar:\
     ${maven.repository}/org/slf4j/slf4j-jdk14/${slf4j.version}/slf4j-jdk14-${slf4j.version}.jar

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java?rev=1483990&r1=1483989&r2=1483990&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] Fri May 17 20:57:05 2013
@@ -26,6 +26,8 @@ import java.io.EOFException;
 import java.nio.ByteBuffer;
 import java.nio.channels.ReadableByteChannel;
 import java.lang.reflect.Array;
+import javax.measure.converter.UnitConverter;
+import javax.measure.converter.ConversionException;
 import org.opengis.parameter.InvalidParameterCardinalityException;
 import org.apache.sis.internal.jdk8.Function;
 import org.apache.sis.internal.netcdf.Decoder;
@@ -33,10 +35,12 @@ import org.apache.sis.internal.netcdf.Va
 import org.apache.sis.internal.netcdf.GridGeometry;
 import org.apache.sis.internal.netcdf.WarningProducer;
 import org.apache.sis.internal.util.CollectionsExt;
+import org.apache.sis.internal.util.Utilities;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.measure.Units;
 
 
 /**
@@ -70,6 +74,12 @@ public final class ChannelDecoder extend
      */
     private static final Locale NAME_LOCALE = Locale.US;
 
+    /**
+     * {@code true} if the default timezone is UTC, or {@code false} if it shall be the
+     * {@linkplain java.util.TimeZone#getDefault() system default}.
+     */
+    private static final boolean DEFAULT_TIMEZONE_IS_UTC = true;
+
     /*
      * NOTE: the names of the static constants below this point match the names used in the Backus-Naur Form (BNF)
      *       definitions in the NetCDF Classic and 64-bit Offset Format (1.0) specification (link in class javdoc),
@@ -586,14 +596,47 @@ public final class ChannelDecoder extend
         return null;
     }
 
+    /**
+     * Returns the value of the attribute of the given name as a date, or {@code null} if none.
+     * If there is more than one numeric value, only the first one is returned.
+     */
     @Override
-    public Date dateValue(String name) throws IOException {
-        throw new UnsupportedOperationException();
+    public Date dateValue(final String name) throws IOException {
+        final Attribute attribute = findAttribute(name);
+        if (attribute != null && attribute.value != null) {
+            if (attribute.value instanceof String) try {
+                return Utilities.parseDateTime((String) attribute.value, DEFAULT_TIMEZONE_IS_UTC);
+            } catch (IllegalArgumentException e) {
+                warning("dateValue", e);
+            }
+        }
+        return null;
     }
 
+    /**
+     * Converts the given numerical values to date, using the information provided in the given unit symbol.
+     * The unit symbol is typically a string like "<cite>days since 1970-01-01T00:00:00Z</cite>".
+     *
+     * @param  values The values to convert. May contains {@code null} elements.
+     * @return The converted values. May contains {@code null} elements.
+     */
     @Override
-    public Date[] numberToDate(String symbol, Number... values) throws IOException {
-        throw new UnsupportedOperationException();
+    public Date[] numberToDate(final String symbol, final Number... values) throws IOException {
+        final Date[] dates = new Date[values.length];
+        final String[] parts = symbol.split("(?i)\\bsince\\b");
+        if (parts.length == 2) try {
+            final UnitConverter converter = Units.valueOf(parts[0]).getConverterToAny(Units.MILLISECOND);
+            final long epoch = Utilities.parseDateTime(parts[1], DEFAULT_TIMEZONE_IS_UTC).getTime();
+            for (int i=0; i<values.length; i++) {
+                final Number value = values[i];
+                if (value != null) {
+                    dates[i] = new Date(epoch + Math.round(converter.convert(value.doubleValue())));
+                }
+            }
+        } catch (ConversionException | IllegalArgumentException e) {
+            warning("numberToDate", e);
+        }
+        return dates;
     }
 
     @Override

Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java?rev=1483990&r1=1483989&r2=1483990&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java [UTF-8] Fri May 17 20:57:05 2013
@@ -18,11 +18,13 @@ package org.apache.sis.internal.netcdf;
 
 import java.util.Date;
 import java.io.IOException;
+import static org.opengis.wrapper.netcdf.IOTestCase.NCEP;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
 import static org.apache.sis.test.TestUtilities.date;
 import static org.apache.sis.storage.netcdf.AttributeNames.*;
+import static org.apache.sis.test.TestUtilities.date;
 
 
 /**
@@ -88,4 +90,23 @@ public strictfp class DecoderTest extend
         assertAttributeEquals(date("2005-09-22 00:00:00"), DATE_CREATED);
         assertAttributeEquals((Date) null,                 DATE_MODIFIED);
     }
+
+    /**
+     * Tests {@link Decoder#numberToDate(String, Number[])}.
+     *
+     * @throws IOException If an error occurred while reading the NetCDF file.
+     */
+    @Test
+    public void testNumberToDate() throws IOException {
+        final Decoder decoder = selectDataset(NCEP);
+        assertArrayEquals(new Date[] {
+            date("2005-09-22 00:00:00")
+        }, decoder.numberToDate("hours since 1992-1-1", 120312));
+
+        assertArrayEquals(new Date[] {
+            date("1970-01-09 18:00:00"),
+            date("1969-12-29 06:00:00"),
+            date("1993-04-10 00:00:00")
+        }, decoder.numberToDate("days since 1970-01-01T00:00:00Z", 8.75, -2.75, 8500));
+    }
 }

Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java?rev=1483990&r1=1483989&r2=1483990&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java [UTF-8] Fri May 17 20:57:05 2013
@@ -62,9 +62,4 @@ public final strictfp class ChannelDecod
     protected boolean isSupplementalFormatSupported(final String format) {
         return false;
     }
-
-    @Override
-    public void testDateValue() {
-        // Disabled for now
-    }
 }