sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1445079 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/ test/java/org/apache/sis/test/suite/
Date Tue, 12 Feb 2013 09:57:36 GMT
Author: desruisseaux
Date: Tue Feb 12 09:57:35 2013
New Revision: 1445079

URL: http://svn.apache.org/r1445079
Log:
Initial port of RangeFormat.

Added:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java   (with props)
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java   (with props)
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java   (with props)
Modified:
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java?rev=1445079&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java Tue Feb 12 09:57:35 2013
@@ -0,0 +1,708 @@
+/*
+ * 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.measure;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.text.Format;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.FieldPosition;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import javax.measure.unit.Unit;
+import javax.measure.unit.UnitFormat;
+import org.apache.sis.util.Numbers;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.UnconvertibleObjectException;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * Parses and formats {@linkplain Range ranges} of the given type. The kind of ranges created
+ * by the {@code parse} method is determined by the class of range components:
+ *
+ * <ul>
+ *   <li>If the components type is assignable to {@link Date}, then the {@code parse} method
+ *       will create {@link DateRange} objects.</li>
+ *   <li>If the components type is assignable to {@link Number}, then the {@code parse} method
+ *       will create {@link MeasurementRange} objects if the text to parse contains a
+ *       {@linkplain Unit unit} of measure, or {@link NumberRange} otherwise.</li>
+ * </ul>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.06)
+ * @version 0.3
+ * @module
+ *
+ * @see Range
+ * @see DateRange
+ * @see NumberRange
+ * @see MeasurementRange
+ */
+public class RangeFormat extends Format {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 6700474540675919894L;
+
+    /**
+     * The constant value for {@link FieldPosition} which designate the minimal value.
+     * This constant can be combined with one of the {@code *_FIELD} constants defined
+     * in {@link NumberFormat} or {@link DateFormat} classes for fetching the position
+     * of a formatted field. For example in order to get the position where the fraction
+     * digits of the {@linkplain Range#getMinValue() minimal value} begin, use:
+     *
+     * {@preformat java
+     *     FieldPosition pos = new FieldPosition(NumberFormat.FRACTION_FIELD | RangeFormat.MIN_VALUE_FIELD);
+     *     rangeFormat.format(range, buffer, pos);
+     *     int beginIndex = pos.getBeginIndex();
+     * }
+     */
+    public static final int MIN_VALUE_FIELD = 0;
+    // Note: the implementation in this class requires that MIN_VALUE_FIELD is 0.
+
+    /**
+     * The constant value for {@link FieldPosition} which designate the maximal value.
+     * This constant can be combined with one of the {@code *_FIELD} constants defined
+     * in {@link NumberFormat} or {@link DateFormat} classes for fetching the position
+     * of a formatted field. For example in order to get the position where the fraction
+     * digits of the {@linkplain Range#getMaxValue() maximal value} begin, use:
+     *
+     * {@preformat java
+     *     FieldPosition pos = new FieldPosition(NumberFormat.FRACTION_FIELD | RangeFormat.MAX_VALUE_FIELD);
+     *     rangeFormat.format(range, buffer, pos);
+     *     int beginIndex = pos.getBeginIndex();
+     * }
+     */
+    public static final int MAX_VALUE_FIELD = 0x40000000;
+    // Note: do not use the sign bit, since the JDK uses -1 for "no field ID".
+    // The maximal value used by the formats (as of JDK 1.6) is 17.
+
+    /**
+     * The constant value for {@link FieldPosition} which designate the units of measurement.
+     * This field can <strong>not</strong> be combined with other field masks.
+     */
+    public static final int UNIT_FIELD = 0x20000000;
+
+    /**
+     * The symbols used for parsing and formatting a range.
+     */
+    private RangeSymbols symbols;
+
+    /**
+     * Symbols used by this format, inferred from {@link DecimalFormatSymbols}.
+     */
+    private final char minusSign;
+
+    /**
+     * Symbols used by this format, inferred from {@link DecimalFormatSymbols}.
+     */
+    private final String infinity;
+
+    /**
+     * The type of the range components. Valid types are {@link Number}, {@link Angle},
+     * {@link Date} or a subclass of those types. This value determines the kind of range
+     * to be created by the parse method:
+     *
+     * <ul>
+     *   <li>{@link NumberRange} if the element class is assignable to {@link Number}.</li>
+     *   <li>{@link DateRange}   if the element class is assignable to {@link Date}.</li>
+     * </ul>
+     */
+    protected final Class<?> elementType;
+
+    /**
+     * The format to use for parsing and formatting the range components.
+     * The format is determined from the {@linkplain #elementType element type}:
+     *
+     * <ul>
+     *   <li>{@link AngleFormat}  if the element class is assignable to {@link Angle}.</li>
+     *   <li>{@link NumberFormat} if the element class is assignable to {@link Number}.</li>
+     *   <li>{@link DateFormat}   if the element class is assignable to {@link Date}.</li>
+     * </ul>
+     */
+    protected final Format elementFormat;
+
+    /**
+     * The format for units of measurement, or {@code null} if none. This is non-null if and
+     * only if {@link #elementType} is assignable to {@link Number} but not to {@link Angle}.
+     */
+    protected final UnitFormat unitFormat;
+
+    /**
+     * Constructs a new {@code RangeFormat} for the default locale.
+     *
+     * @return A range format in the default locale.
+     */
+    public static RangeFormat getInstance() {
+        return new RangeFormat();
+    }
+
+    /**
+     * Constructs a new {@code RangeFormat} for the specified locale.
+     *
+     * @param  locale The locale.
+     * @return A range format in the given locale.
+     */
+    public static RangeFormat getInstance(final Locale locale) {
+        return new RangeFormat(locale);
+    }
+
+    /**
+     * Creates a new format for parsing and formatting {@linkplain NumberRange number ranges}
+     * using the {@linkplain Locale#getDefault() default locale}.
+     */
+    public RangeFormat() {
+        this(Locale.getDefault(Locale.Category.FORMAT));
+    }
+
+    /**
+     * Creates a new format for parsing and formatting {@linkplain NumberRange number ranges}
+     * using the given locale.
+     *
+     * @param  locale The locale for parsing and formatting range components.
+     */
+    public RangeFormat(final Locale locale) {
+        this(locale, Number.class);
+    }
+
+    /**
+     * Creates a new format for parsing and formatting {@linkplain DateRange date ranges}
+     * using the given locale and timezone.
+     *
+     * @param locale   The locale for parsing and formatting range components.
+     * @param timezone The timezone for the date to be formatted.
+     */
+    public RangeFormat(final Locale locale, final TimeZone timezone) {
+        this(locale, Date.class);
+        ((DateFormat) elementFormat).setTimeZone(timezone);
+    }
+
+    /**
+     * Creates a new format for parsing and formatting {@linkplain Range ranges} of
+     * the given element class using the given locale. The element class is typically
+     * {@code Date.class} or some subclass of {@code Number.class}.
+     *
+     * @param  locale The locale for parsing and formatting range components.
+     * @param  elementType The type of range components.
+     * @throws IllegalArgumentException If the given type is not recognized by this constructor.
+     */
+    public RangeFormat(final Locale locale, final Class<?> elementType) throws IllegalArgumentException {
+        this.elementType = elementType;
+        if (Angle.class.isAssignableFrom(elementType)) {
+            elementFormat = AngleFormat.getInstance(locale);
+            unitFormat    = null;
+        } else if (Number.class.isAssignableFrom(elementType)) {
+            elementFormat = NumberFormat.getNumberInstance(locale);
+            unitFormat    = UnitFormat.getInstance(locale);
+        } else if (Date.class.isAssignableFrom(elementType)) {
+            elementFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
+            unitFormat    = null;
+        } else {
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, elementType));
+        }
+        final DecimalFormatSymbols ds;
+        if (elementFormat instanceof DecimalFormat) {
+            ds = ((DecimalFormat) elementFormat).getDecimalFormatSymbols();
+        } else {
+            ds = DecimalFormatSymbols.getInstance(locale);
+        }
+        minusSign = ds.getMinusSign();
+        infinity  = ds.getInfinity();
+        symbols   = new RangeSymbols();
+    }
+
+    /**
+     * Returns the symbols used for parsing and formatting ranges.
+     *
+     * @return The symbols used by this format.
+     */
+    public RangeSymbols getSymbols() {
+        return symbols.clone();
+    }
+
+    /**
+     * Sets the symbols to use for parsing and formatting ranges.
+     *
+     * @param symbols The new symbols to use for this format.
+     */
+    public void setSymbols(final RangeSymbols symbols) {
+        this.symbols = symbols.clone();
+    }
+
+    /**
+     * Returns the pattern used by {@link #elementFormat} for formatting the minimum and
+     * maximum values. If the element format does not use pattern, returns {@code null}.
+     *
+     * @param  localized {@code true} for returning the localized pattern, or {@code false}
+     *         for the unlocalized one.
+     * @return The pattern, or {@code null} if the {@link #elementFormat} doesn't use pattern.
+     */
+    public String getElementPattern(final boolean localized) {
+        final Format format = elementFormat;
+        if (format instanceof DecimalFormat) {
+            final DecimalFormat df = (DecimalFormat) format;
+            return localized ? df.toLocalizedPattern() : df.toPattern();
+        }
+        if (format instanceof SimpleDateFormat) {
+            final SimpleDateFormat df = (SimpleDateFormat) format;
+            return localized ? df.toLocalizedPattern() : df.toPattern();
+        }
+        if (format instanceof AngleFormat) {
+            return ((AngleFormat) format).toPattern();
+        }
+        return null;
+    }
+
+    /**
+     * Sets the pattern to be used by {@link #elementFormat} for formatting the minimum and
+     * maximum values.
+     *
+     * @param  pattern The new pattern.
+     * @param  localized {@code true} if the given pattern is localized.
+     * @throws IllegalStateException If the {@link #elementFormat} does not use pattern.
+     */
+    public void setElementPattern(final String pattern, final boolean localized) {
+        final Format format = elementFormat;
+        if (format instanceof DecimalFormat) {
+            final DecimalFormat df = (DecimalFormat) format;
+            if (localized) {
+                df.applyLocalizedPattern(pattern);
+            } else {
+                df.applyPattern(pattern);
+            }
+        } else if (format instanceof SimpleDateFormat) {
+            final SimpleDateFormat df = (SimpleDateFormat) format;
+            if (localized) {
+                df.applyLocalizedPattern(pattern);
+            } else {
+                df.applyPattern(pattern);
+            }
+        } else if (format instanceof AngleFormat) {
+            ((AngleFormat) format).applyPattern(pattern);
+        } else {
+            throw new IllegalStateException();
+        }
+    }
+
+    /**
+     * Formats a {@link Range} and appends the resulting text to a given string buffer. The default
+     * implementation formats the range using the same rules than {@link Range#toString()}, except
+     * that the values (numbers, angles or dates) are formatted using the {@link Format} object
+     * appropriate for the locale given at construction time.
+     *
+     * @param  range      The {@link Range} object to format.
+     * @param  toAppendTo Where the text is to be appended.
+     * @param  pos        Identifies a field in the formatted text.
+     * @return The string buffer passed in as {@code toAppendTo}, with formatted text appended.
+     * @throws IllegalArgumentException If this formatter can not format the given object.
+     */
+    @Override
+    public StringBuffer format(final Object range, final StringBuffer toAppendTo, final FieldPosition pos) {
+        if (!(range instanceof Range<?>)) {
+            final String message;
+            if (range == null) {
+                message = Errors.format(Errors.Keys.NullArgument_1, "range");
+            } else {
+                message = Errors.format(Errors.Keys.IllegalArgumentClass_3, "range", Range.class, range.getClass());
+            }
+            throw new IllegalArgumentException(message);
+        }
+        /*
+         * Special case for an empty range. This is typically formatted as "[]". The field
+         * position is unconditionally set to the empty substring inside the brackets.
+         */
+        final Range<?> r = (Range<?>) range;
+        final RangeSymbols s = symbols;
+        if (r.isEmpty()) {
+            toAppendTo.append(s.openInclusive);
+            final int p = toAppendTo.length();
+            pos.setBeginIndex(p); // First index, inclusive.
+            pos.setEndIndex  (p); // Last index, exclusive
+            return toAppendTo.append(s.closeInclusive);
+        }
+        /*
+         * Prepares the FieldPosition for the minimal and the maximal values. We need to
+         * ensure that those two FieldPositions have their MAX_VALUE_FIELD bit cleared.
+         * We opportunistically reuse the FieldPosition provided by the user if suitable
+         * (this approach assumes that MIN_VALUE_FIELD is zero).
+         */
+        final FieldPosition minPos, maxPos;
+        final int fieldID = pos.getField();
+        if ((fieldID & MAX_VALUE_FIELD) == 0) {
+            minPos = pos; // User is interested in minimal value.
+            maxPos = new FieldPosition(fieldID);
+        } else {
+            minPos = new FieldPosition(fieldID & ~MAX_VALUE_FIELD);
+            maxPos = minPos; // Will overwrite the value of minPos.
+        }
+        final Comparable<?> minValue = r.getMinValue();
+        final Comparable<?> maxValue = r.getMaxValue();
+        if (minValue != null && minValue.equals(maxValue)) {
+            /*
+             * Special case: minimal and maximal values are the same.  Formats only the minimal
+             * value. If the user asked for the position of the maximal value, then the indexes
+             * of the minimal value (which is also the maximal value) will be copied at the end
+             * of this method (this work because maxPos == minPos in such case).
+             */
+            elementFormat.format(minValue, toAppendTo, minPos);
+        } else {
+            /*
+             * General case: format the minimal and maximal values between brackets.
+             * Units of measurement are added in the range is actually a MeasurementRange.
+             */
+            toAppendTo.append(r.isMinIncluded() ? s.openInclusive : s.openExclusive);
+            if (minValue == null) {
+                toAppendTo.append(minusSign);
+                minPos.setBeginIndex(toAppendTo.length());
+                toAppendTo.append(infinity);
+                minPos.setEndIndex(toAppendTo.length());
+            } else {
+                elementFormat.format(minValue, toAppendTo, minPos);
+            }
+            toAppendTo.append(' ').append(s.separator).append(' ');
+            if (maxValue == null) {
+                maxPos.setBeginIndex(toAppendTo.length());
+                toAppendTo.append(infinity);
+                maxPos.setEndIndex(toAppendTo.length());
+            } else {
+                elementFormat.format(maxValue, toAppendTo, maxPos);
+            }
+            toAppendTo.append(r.isMaxIncluded() ? s.closeInclusive : s.closeExclusive);
+        }
+        /*
+         * If the user asked for the position of the minimal value, then 'pos' is already defined
+         * correctly because 'minPos == pos'. If the user asked for the position of the maximal
+         * value, then we need to copy the indexes from the 'maxPos' instance.
+         */
+        if (pos != minPos) {
+            pos.setBeginIndex(maxPos.getBeginIndex());
+            pos.setEndIndex  (maxPos.getEndIndex());
+        }
+        /*
+         * Formats the unit, if there is any. Note that the above lines processed UNIT_FIELD as
+         * if it was MIN_VALUE_FIELD with some code not recognized by the formatter, so we need
+         * to overwrite those indexes below in such case.
+         */
+        final boolean isUnitField = (pos.getField() == UNIT_FIELD);
+        if (unitFormat != null && range instanceof MeasurementRange<?>) {
+            final Unit<?> units = ((MeasurementRange<?>) range).getUnits();
+            if (units != null) {
+                toAppendTo.append(' ');
+                if (isUnitField) {
+                    pos.setBeginIndex(toAppendTo.length());
+                }
+                unitFormat.format(units, toAppendTo, pos);
+                if (isUnitField) {
+                    pos.setEndIndex(toAppendTo.length());
+                }
+                return toAppendTo;
+            }
+        }
+        if (isUnitField) {
+            final int length = toAppendTo.length();
+            pos.setBeginIndex(length);
+            pos.setEndIndex  (length);
+        }
+        return toAppendTo;
+    }
+
+    /**
+     * Parses text from a string to produce a range. The default implementation delegates to
+     * {@link #parse(String)} with no additional work.
+     *
+     * @param  source The text, part of which should be parsed.
+     * @return A range parsed from the string, or {@code null} in case of error.
+     * @throws ParseException If the given string can not be fully parsed.
+     */
+    @Override
+    public Object parseObject(final String source) throws ParseException {
+        return parse(source);
+    }
+
+    /**
+     * Parses text from a string to produce a range. The default implementation delegates to
+     * {@link #parse(String, ParsePosition)} with no additional work.
+     *
+     * @param  source The text, part of which should be parsed.
+     * @param  pos    Index and error index information as described above.
+     * @return A range parsed from the string, or {@code null} in case of error.
+     */
+    @Override
+    public Object parseObject(final String source, final ParsePosition pos) {
+        return parse(source, pos);
+    }
+
+    /**
+     * Parses text from the given string to produce a range. This method use the full string.
+     * If there is some unparsed characters after the parsed range, then this method thrown an
+     * exception.
+     *
+     * @param  source The text to parse.
+     * @return The parsed range (never {@code null}).
+     * @throws ParseException If the given string can not be fully parsed.
+     */
+    public Range<?> parse(final String source) throws ParseException {
+        final ParsePosition pos = new ParsePosition(0);
+        UnconvertibleObjectException failure = null;
+        try {
+            final Range<?> range = tryParse(source, pos);
+            if (range != null) {
+                return range;
+            }
+        } catch (UnconvertibleObjectException e) {
+            failure = e;
+        }
+        final int errorIndex = pos.getErrorIndex();
+        final ParseException e = new ParseException(Errors.format(Errors.Keys.UnparsableStringForClass_3,
+                elementType, source, CharSequences.token(source, errorIndex)), errorIndex);
+        e.initCause(failure);
+        throw e;
+    }
+
+    /**
+     * Parses text from a string to produce a range. The method attempts to parse text starting
+     * at the index given by {@code pos}. If parsing succeeds, then the index of {@code pos} is
+     * updated to the index after the last character used, and the parsed range is returned. If
+     * an error occurs, then the index of {@code pos} is not changed, the error index of {@code pos}
+     * is set to the index of the character where the error occurred, and {@code null} is returned.
+     *
+     * @param  source The text, part of which should be parsed.
+     * @param  pos    Index and error index information as described above.
+     * @return A range parsed from the string, or {@code null} in case of error.
+     */
+    public Range<?> parse(final String source, final ParsePosition pos) {
+        final int origin = pos.getIndex();
+        Range<?> range;
+        try {
+            // Remainder: tryParse may return null.
+            range = tryParse(source, pos);
+        } catch (UnconvertibleObjectException e) {
+            // Ignore - the error will be reported through the error index.
+            range = null;
+        }
+        if (range != null) {
+            pos.setErrorIndex(-1);
+        } else {
+            pos.setIndex(origin);
+        }
+        return range;
+    }
+
+    /**
+     * Tries to parse the given text. In case of success, the error index is undetermined and
+     * need to be reset to -1.  In case of failure (including an exception being thrown), the
+     * parse index is undetermined and need to be reset to its initial value.
+     */
+    private Range<?> tryParse(final String source, final ParsePosition pos)
+            throws UnconvertibleObjectException
+    {
+        final int length = source.length();
+        int index = pos.getIndex();
+        /*
+         * Skip leading whitespace and find the first non-blank character.  It is usually
+         * an opening bracket, except if minimal and maximal values are the same in which
+         * case the brackets may be omitted.
+         */
+        char c;
+        do if (index >= length) {
+            pos.setErrorIndex(length);
+            return null;
+        } while ((Character.isWhitespace(c = source.charAt(index++))));
+        /*
+         * Get the minimal and maximal values, and whatever they are inclusive or exclusive.
+         */
+        final RangeSymbols s = symbols;
+        final Object minValue, maxValue;
+        final boolean isMinIncluded, isMaxIncluded;
+        if (!s.isOpen(c)) {
+            /*
+             * No bracket. Assume that we have a single value for the range.
+             */
+            pos.setIndex(index - 1);
+            final Object value = elementFormat.parseObject(source, pos);
+            if (value == null) {
+                return null;
+            }
+            pos.setErrorIndex(index - 1); // In case of failure during the conversion.
+            minValue = maxValue = convert(value);
+            isMinIncluded = isMaxIncluded = true;
+            index = pos.getIndex();
+        } else {
+            /*
+             * We found an opening bracket. Skip the whitespaces. If the next
+             * character is a closing bracket, then we have an empty range.
+             */
+            isMinIncluded = (c == s.openInclusive);
+            do if (index >= length) {
+                pos.setErrorIndex(length);
+                return null;
+            } while ((Character.isWhitespace(c = source.charAt(index++))));
+            if (s.isClose(c)) {
+                pos.setIndex(index);
+                pos.setErrorIndex(index - 1); // In case of failure during the conversion.
+                minValue = maxValue = convert(0);
+                isMaxIncluded = false;
+            } else {
+                /*
+                 * At this point, we have determined that the range is non-empty and there
+                 * is at least one value to parse. First, parse the minimal value. If we
+                 * fail to parse, check if it was the infinity value (note that infinity
+                 * should have been parsed successfully if the format is DecimalFormat).
+                 */
+                pos.setIndex(index - 1);
+                Object value = elementFormat.parseObject(source, pos);
+                if (value == null) {
+                    if (c != minusSign) {
+                        index--;
+                    }
+                    if (!source.regionMatches(index, infinity, 0, infinity.length())) {
+                        return null;
+                    }
+                    pos.setIndex(index += infinity.length());
+                }
+                pos.setErrorIndex(index - 1); // In case of failure during the conversion.
+                minValue = convert(value);
+                /*
+                 * Parsing of minimal value succeed and its type is valid. Now look for the
+                 * separator. If it is not present, then assume that we have a single value
+                 * for the range. The default RangeFormat implementation does not format
+                 * brackets in such case (see the "No bracket" case above), but we make the
+                 * parser tolerant to the case where the brackets are present.
+                 */
+                index = pos.getIndex();
+                do if (index >= length) {
+                    pos.setErrorIndex(length);
+                    return null;
+                } while ((Character.isWhitespace(c = source.charAt(index++))));
+                final String separator = s.separator;
+                if (source.regionMatches(index-1, separator, 0, separator.length())) {
+                    index += separator.length() - 1;
+                    do if (index >= length) {
+                        pos.setErrorIndex(length);
+                        return null;
+                    } while ((Character.isWhitespace(c = source.charAt(index++))));
+                    pos.setIndex(index - 1);
+                    value = elementFormat.parseObject(source, pos);
+                    if (value == null) {
+                        if (!source.regionMatches(--index, infinity, 0, infinity.length())) {
+                            return null;
+                        }
+                        pos.setIndex(index += infinity.length());
+                    }
+                    pos.setErrorIndex(index - 1); // In case of failure during the conversion.
+                    maxValue = convert(value);
+                    /*
+                     * Skip one last time the whitespaces. The check for the closing bracket
+                     * (which is mandatory) is performed outside the "if" block since it is
+                     * common to the two "if ... else" cases.
+                     */
+                    index = pos.getIndex();
+                    do if (index >= length) {
+                        pos.setErrorIndex(length);
+                        return null;
+                    } while ((Character.isWhitespace(c = source.charAt(index++))));
+                } else {
+                    maxValue = minValue;
+                }
+                if (!s.isClose(c)) {
+                    pos.setErrorIndex(index - 1);
+                    return null;
+                }
+                isMaxIncluded = (c == s.closeInclusive);
+            }
+            pos.setIndex(index);
+        }
+        /*
+         * Parses the unit, if any. The units are always optional: if we can not parse
+         * them, then we will consider that the parsing stopped before the units.
+         */
+        Unit<?> unit = null;
+        if (unitFormat != null) {
+            while (index < length) {
+                if (Character.isWhitespace(source.charAt(index))) {
+                    index++;
+                    continue;
+                }
+                // At this point we found a character that could be
+                // the beginning of a unit symbol. Try to parse that.
+                pos.setIndex(index);
+// TODO: Uncomment when we have upgrated JSR-275 dependency.
+//              unit = unitFormat.parse(source, pos);
+                break;
+            }
+        }
+        /*
+         * At this point, all required informations are available. Now build the range.
+         * In the special case were the target type is the generic Number type instead
+         * than a more specialized type, the finest suitable type will be determined.
+         */
+        if (Number.class.isAssignableFrom(elementType)) {
+            @SuppressWarnings({"unchecked","rawtypes"})
+            Class<? extends Number> type = (Class) elementType;
+            Number min = (Number) minValue;
+            Number max = (Number) maxValue;
+            if (type == Number.class) {
+                type = Numbers.widestClass(Numbers.narrowestClass(min), Numbers.narrowestClass(max));
+                min  = Numbers.cast(min, type);
+                max  = Numbers.cast(max, type);
+            }
+            if (min.doubleValue() == Double.NEGATIVE_INFINITY) min = null;
+            if (max.doubleValue() == Double.POSITIVE_INFINITY) max = null;
+            if (unit != null) {
+                @SuppressWarnings({"unchecked","rawtypes"})
+                final MeasurementRange<?> range = new MeasurementRange(type, min, isMinIncluded, max, isMaxIncluded, unit);
+                return range;
+            }
+            @SuppressWarnings({"unchecked","rawtypes"})
+            final NumberRange<?> range = new NumberRange(type, min, isMinIncluded, max, isMaxIncluded);
+            return range;
+        } else if (Date.class.isAssignableFrom(elementType)) {
+            final Date min = (Date) minValue;
+            final Date max = (Date) maxValue;
+            return new DateRange(min, isMinIncluded, max, isMaxIncluded);
+        } else {
+            @SuppressWarnings({"unchecked","rawtypes"})
+            final Class<? extends Comparable<?>> type = (Class) elementType;
+            final Comparable<?> min = (Comparable<?>) minValue;
+            final Comparable<?> max = (Comparable<?>) maxValue;
+            @SuppressWarnings({"unchecked","rawtypes"})
+            final Range<?> range = new Range(type, min, isMinIncluded, max, isMaxIncluded);
+            return range;
+        }
+    }
+
+    /**
+     * Converts the given value to the a {@link #elementType} type.
+     */
+    @SuppressWarnings("unchecked")
+    private Object convert(final Object value) throws UnconvertibleObjectException {
+        if (value == null || elementType.isInstance(value)) {
+            return value;
+        }
+        if (value instanceof Number && Number.class.isAssignableFrom(elementType)) {
+            return Numbers.cast((Number) value, (Class<? extends Number>) elementType);
+        }
+        throw new UnconvertibleObjectException(Errors.format(
+                Errors.Keys.IllegalClass_2, elementType, value.getClass()));
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java?rev=1445079&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java (added)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java Tue Feb 12 09:57:35 2013
@@ -0,0 +1,109 @@
+/*
+ * 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.measure;
+
+
+/**
+ * Symbols used by {@link RangeFormat} when parsing and formatting a range.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.06)
+ * @version 0.3
+ * @module
+ *
+ * @see RangeFormat
+ */
+public class RangeSymbols implements Cloneable {
+    /**
+     * The character opening a range in which the minimal value is inclusive.
+     * The default value is {@code '['}.
+     */
+    public char openInclusive = '[';
+
+    /**
+     * The character opening a range in which the minimal value is exclusive.
+     * The default value is {@code '('}. Note that the {@code ']'} character
+     * is also sometime used.
+     */
+    public char openExclusive = '(';
+
+    /**
+     * An alternative character opening a range in which the minimal value is exclusive.
+     * This character is not used for formatting (only {@link #openExclusive} is used),
+     * but is accepted during parsing. The default value is {@code ']'}.
+     */
+    public char openExclusiveAlt = ']';
+
+    /**
+     * The character closing a range in which the maximal value is inclusive.
+     * The default value is {@code ']'}.
+     */
+    public char closeInclusive = ']';
+
+    /**
+     * The character closing a range in which the maximal value is exclusive.
+     * The default value is {@code ')'}. Note that the {@code '['} character
+     * is also sometime used.
+     */
+    public char closeExclusive = ')';
+
+    /**
+     * An alternative character closing a range in which the maximal value is exclusive.
+     * This character is not used for formatting (only {@link #closeExclusive} is used),
+     * but is accepted during parsing. The default value is {@code '['}.
+     */
+    public char closeExclusiveAlt = '[';
+
+    /**
+     * The string to use as a separator between minimal and maximal value, not including
+     * whitespaces. The default value is {@code "…"} (Unicode 2026).
+     */
+    public String separator = "…";
+
+    /**
+     * Creates a new set of range symbols initialized to their default values.
+     */
+    public RangeSymbols() {
+    }
+
+    /**
+     * Returns {@code true} if the given character is any of the opening bracket characters.
+     */
+    final boolean isOpen(final char c) {
+        return (c == openInclusive) || (c == openExclusive) || (c == openExclusiveAlt);
+    }
+
+    /**
+     * Returns {@code true} if the given character is any of the closing bracket characters.
+     */
+    final boolean isClose(final char c) {
+        return (c == closeInclusive) || (c == closeExclusive) || (c == closeExclusiveAlt);
+    }
+
+    /**
+     * Returns a clone of this set of symbols.
+     */
+    @Override
+    public RangeSymbols clone() {
+        try {
+            return (RangeSymbols) super.clone();
+        } catch (CloneNotSupportedException e) {
+            // Should never happen since we are cloneable.
+            throw new AssertionError(e);
+        }
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeSymbols.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java?rev=1445079&view=auto
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java (added)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java Tue Feb 12 09:57:35 2013
@@ -0,0 +1,304 @@
+/*
+ * 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.measure;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.ParseException;
+import javax.measure.unit.SI;
+import org.junit.Test;
+import org.apache.sis.test.TestCase;
+import org.apache.sis.test.DependsOn;
+
+import static org.junit.Assert.*;
+import static java.lang.StrictMath.*;
+import static java.lang.Double.POSITIVE_INFINITY;
+import static java.lang.Double.NEGATIVE_INFINITY;
+
+
+/**
+ * Tests parsing and formatting done by the {@link RangeFormat} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.06)
+ * @version 0.3
+ * @module
+ */
+@DependsOn(MeasurementRangeTest.class)
+public final strictfp class RangeFormatTest extends TestCase {
+    /**
+     * The format being tested.
+     */
+    private RangeFormat format;
+
+    /**
+     * The position of the minimal value and maximal value fields.
+     */
+    private FieldPosition minPos, maxPos;
+
+    /**
+     * The position during parsing.
+     */
+    private ParsePosition parsePos;
+
+    /**
+     * Formats the given range.
+     */
+    private String format(final Range<?> range) {
+        final String s1 = format.format(range, new StringBuffer(), minPos).toString();
+        final String s2 = format.format(range, new StringBuffer(), maxPos).toString();
+        assertEquals("Two consecutive formats produced different results.", s1, s2);
+        return s1;
+    }
+
+    /**
+     * Parses the given text and ensure that there is only whitespace or underscore after the last
+     * parse position. Also verifies that there is no underscore before the parse position. This
+     * assume that the underscore is not allowed to appears in the valid portion of the text.
+     */
+    private Range<?> parse(final String text) {
+        parsePos.setIndex(0);
+        final Range<?> range = format.parse(text, parsePos);
+        assertEquals("Index position shall be modified on parse success, and only parse success.", parsePos.getIndex()      == 0, range == null);
+        assertEquals("Error position shall be defined on parse failure, and only parse failure",   parsePos.getErrorIndex() >= 0, range == null);
+        if (range != null) {
+            int i = parsePos.getIndex();
+            assertTrue("The parse method parsed a character that it shouldn't have accepted.", text.lastIndexOf('_', i-1) < 0);
+            while (i < text.length()) {
+                final char c = text.charAt(i++);
+                assertTrue("Looks like that the parse method didn't parsed everything.",
+                           Character.isWhitespace(c) || c == '_');
+            }
+        }
+        return range;
+    }
+
+    /**
+     * Tests the {@link RangeFormat#format} method with numbers.
+     */
+    @Test
+    public void testFormatNumber() {
+        format = new RangeFormat(Locale.CANADA);
+        minPos = new FieldPosition(RangeFormat.MIN_VALUE_FIELD | NumberFormat.INTEGER_FIELD);
+        maxPos = new FieldPosition(RangeFormat.MAX_VALUE_FIELD | NumberFormat.INTEGER_FIELD);
+
+        // Closed range
+        assertEquals("[-10 … 20]", format(NumberRange.create(-10, 20)));
+        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   4, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 7, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   9, maxPos.getEndIndex());
+
+        // Open range
+        assertEquals("(-3 … 4)", format(NumberRange.create(-3, false, 4, false)));
+        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   7, maxPos.getEndIndex());
+
+        // Half-open range
+        assertEquals("[2 … 8)", format(NumberRange.create(2, true, 8, false)));
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   2, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 5, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   6, maxPos.getEndIndex());
+
+        // Half-open range
+        assertEquals("(40 … 90]", format(NumberRange.create(40, false, 90, true)));
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   8, maxPos.getEndIndex());
+
+        // Single value
+        assertEquals("300", format(NumberRange.create(300, 300)));
+        assertEquals("minPos.beginIndex", 0, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 0, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   3, maxPos.getEndIndex());
+
+        // Empty range
+        assertEquals("[]", format(NumberRange.create(300, true, 300, false)));
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   1, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 1, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   1, maxPos.getEndIndex());
+
+        // Negative infinity
+        assertEquals("(-∞ … 30]", format(NumberRange.create(Double.NEGATIVE_INFINITY, 30)));
+        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   8, maxPos.getEndIndex());
+
+        // Positive infinity
+        assertEquals("[50 … ∞)", format(NumberRange.create(50, Double.POSITIVE_INFINITY)));
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   7, maxPos.getEndIndex());
+
+        // Positive infinities
+        assertEquals("(-∞ … ∞)", format(NumberRange.create(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)));
+        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   7, maxPos.getEndIndex());
+
+        // Positive infinity with integers
+        assertEquals("[50 … ∞)", format(new NumberRange<>(Integer.class, 50, null)));
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   7, maxPos.getEndIndex());
+
+        // Negative infinity with integers
+        assertEquals("(-∞ … 40]", format(new NumberRange<>(Integer.class, null, 40)));
+        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   8, maxPos.getEndIndex());
+
+        // Measurement
+        assertEquals("[-10 … 20] m", format(MeasurementRange.create(-10, 20, SI.METRE)));
+        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.endIndex",   4, minPos.getEndIndex());
+        assertEquals("maxPos.beginIndex", 7, maxPos.getBeginIndex());
+        assertEquals("maxPos.endIndex",   9, maxPos.getEndIndex());
+
+        maxPos = new FieldPosition(RangeFormat.UNIT_FIELD);
+        assertEquals("[-1 … 2] km", format(MeasurementRange.create(-1, 2, SI.KILOMETRE)));
+        assertEquals("unitPos.beginIndex", 9, maxPos.getBeginIndex());
+        assertEquals("unitPos.endIndex",  11, maxPos.getEndIndex());
+    }
+
+    /**
+     * Tests the parsing method on ranges of numbers. This test fixes the type to
+     * {@code Integer.class}.  A different test will let the parser determine the
+     * type itself.
+     */
+    @Test
+    public void testParseInteger() {
+        format   = new RangeFormat(Locale.CANADA, Integer.class);
+        parsePos = new ParsePosition(0);
+
+        assertEquals(NumberRange.create(-10,         20       ), parse("[-10 … 20]" ));
+        assertEquals(NumberRange.create( -3, false,   4, false), parse("( -3 …  4) "));
+        assertEquals(NumberRange.create(  2, true,    8, false), parse("  [2 …  8) _"));
+        assertEquals(NumberRange.create( 40, false,  90, true ), parse(" (40 … 90]_"));
+        assertEquals(NumberRange.create(300,        300       ), parse(" 300_"));
+        assertEquals(NumberRange.create(300,        300       ), parse("[300]"));
+        assertEquals(NumberRange.create(300, false, 300, false), parse("(300)"));
+        assertEquals(NumberRange.create(  0, true,    0, false), parse("[]"));
+    }
+
+    /**
+     * Tests the parsing method on ranges of numbers. This test fixes the type to
+     * {@code Double.class}.   A different test will let the parser determine the
+     * type itself.
+     */
+    @Test
+    public void testParseDouble() {
+        format   = new RangeFormat(Locale.CANADA, Double.class);
+        parsePos = new ParsePosition(0);
+
+        assertEquals(NumberRange.create(-10.0,             20.0), parse("[-10 … 20]" ));
+        assertEquals(NumberRange.create(NEGATIVE_INFINITY, 30.0), parse("[-∞ … 30]"));
+        assertEquals(NumberRange.create(50.0, POSITIVE_INFINITY), parse("[50 … ∞]"));
+    }
+
+    /**
+     * Tests the parsing method on ranges of numbers where the type is inferred automatically.
+     */
+    @Test
+    @SuppressWarnings("cast")
+    public void testParseAuto() {
+        format   = new RangeFormat(Locale.CANADA);
+        parsePos = new ParsePosition(0);
+
+        assertEquals(NumberRange.create((byte)    -10, (byte)    20), parse("[  -10 …    20]" ));
+        assertEquals(NumberRange.create((short) -1000, (short) 2000), parse("[-1000 …  2000]" ));
+        assertEquals(NumberRange.create((int)      10, (int)  40000), parse("[   10 … 40000]" ));
+        assertEquals(NumberRange.create((int)       1, (int)  50000), parse("[ 1.00 … 50000]" ));
+        assertEquals(NumberRange.create((float)   8.5, (float)    4), parse("[ 8.50 …     4]" ));
+    }
+
+    /**
+     * Tests the parsing of invalid ranges.
+     */
+    @Test
+    public void testParseFailure() {
+        format   = new RangeFormat(Locale.CANADA);
+        parsePos = new ParsePosition(0);
+
+        assertNull(parse("[-A … 20]")); assertEquals(1, parsePos.getErrorIndex());
+        assertNull(parse("[10 … TB]")); assertEquals(6, parsePos.getErrorIndex());
+        assertNull(parse("[10 x 20]")); assertEquals(4, parsePos.getErrorIndex());
+        assertNull(parse("[10 … 20" )); assertEquals(8, parsePos.getErrorIndex());
+        try {
+            assertNull(format.parse("[10 … TB]"));
+            fail("Parsing should have failed.");
+        } catch (ParseException e) {
+            // This is the expected exception.
+            assertEquals(6, e.getErrorOffset());
+        }
+    }
+
+    /**
+     * Tests formatting and parsing with dates.
+     */
+    @Test
+    public void testDateRange() {
+        format   = new RangeFormat(Locale.FRANCE, TimeZone.getTimeZone("UTC"));
+        minPos   = new FieldPosition(RangeFormat.MIN_VALUE_FIELD | DateFormat.YEAR_FIELD);
+        maxPos   = new FieldPosition(RangeFormat.MAX_VALUE_FIELD | DateFormat.YEAR_FIELD);
+        parsePos = new ParsePosition(0);
+
+        final long HOUR = 60L * 60 * 1000;
+        final long DAY  = 24L * HOUR;
+        final long YEAR = round(365.25 * DAY);
+
+        DateRange range = new DateRange(new Date(15*DAY + 18*HOUR), new Date(20*YEAR + 15*DAY + 9*HOUR));
+        String text =  format(range);
+        assertEquals("[16/01/70 18:00 … 16/01/90 09:00]", text);
+        assertEquals(range, parse(text));
+        /*
+         * Following is for a visual check of the default toString() method,
+         * but is not part of the test suite because it may vary.
+         */
+        if (false) {
+            System.out.println(range);
+        }
+        /*
+         * Try again with the infinity symbol in one bounds.
+         */
+        range = new DateRange((Date) null, new Date(20*YEAR));
+        text  = format(range);
+        assertEquals("(-∞ … 01/01/90 00:00]", text);
+        assertEquals(range, parse(text));
+
+        range = new DateRange(new Date(20*YEAR), (Date) null);
+        text  = format(range);
+        assertEquals("[01/01/90 00:00 … ∞)", text);
+        assertEquals(range, parse(text));
+    }
+}

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1445079&r1=1445078&r2=1445079&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java (original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java Tue Feb 12 09:57:35 2013
@@ -76,6 +76,7 @@ import org.junit.runners.Suite;
     org.apache.sis.measure.NumberRangeTest.class,
     org.apache.sis.measure.MeasurementRangeTest.class,
     org.apache.sis.measure.FormattedCharacterIteratorTest.class,
+    org.apache.sis.measure.RangeFormatTest.class,
     org.apache.sis.measure.AngleFormatTest.class,
     org.apache.sis.measure.AngleTest.class,
     org.apache.sis.internal.util.X364Test.class,



Mime
View raw message