sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1445183 - in /sis/branches/JDK7/sis-utility/src: main/java/org/apache/sis/measure/ test/java/org/apache/sis/measure/
Date Tue, 12 Feb 2013 14:37:44 GMT
Author: desruisseaux
Date: Tue Feb 12 14:37:44 2013
New Revision: 1445183

URL: http://svn.apache.org/r1445183
Log:
Implement RangeFormat.formatToCharacterIterator(Object).

Modified:
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java
    sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/package-info.java
    sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1445183&r1=1445182&r2=1445183&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java Tue
Feb 12 14:37:44 2013
@@ -913,7 +913,7 @@ scan:   for (int i=0; i<length;) {
              * heavy formatToCharacterIterator(…). Otherwise the usual format(…)
method fits well.
              */
             final int startPosition = toAppendTo.length();
-            if (characterIterator instanceof FormattedCharacterIterator) {
+            if (characterIterator != null) {
                 final FormattedCharacterIterator it = (FormattedCharacterIterator) characterIterator;
                 it.append(numberFormat.formatToCharacterIterator(value), toAppendTo);
                 if (suffix != null) {
@@ -1003,7 +1003,7 @@ scan:   for (int i=0; i<length;) {
             pos.setBeginIndex(startPosition);
             pos.setEndIndex(toAppendTo.length());
         }
-        if (characterIterator instanceof FormattedCharacterIterator) {
+        if (characterIterator != null) {
             ((FormattedCharacterIterator) characterIterator).addFieldLimit(
                     Field.HEMISPHERE, suffix, startPosition);
         }

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java?rev=1445183&r1=1445182&r2=1445183&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/Range.java Tue Feb
12 14:37:44 2013
@@ -662,12 +662,12 @@ public class Range<T extends Comparable<
     }
 
     /**
-     * Returns a string representation of this range. The string representation is defined
-     * as below:
+     * Returns a string representation of this range.
+     * The string representation is defined as below:
      *
      * <ul>
      *   <li>If the range is empty, then this method returns {@code "[]"}.</li>
-     *   <li>Otherwise if the minimal value is equals to the maximal values, then
+     *   <li>Otherwise if the minimal value is equals to the maximal value, then
      *       the string representation of that value is returned directly.</li>
      *   <li>Otherwise the string representation of the minimal and maximal values
      *       are formatted like {@code [min … max]} for inclusive bounds or

Modified: 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=1445183&r1=1445182&r2=1445183&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/RangeFormat.java Tue
Feb 12 14:37:44 2013
@@ -21,13 +21,14 @@ import java.util.Locale;
 import java.util.TimeZone;
 import java.text.Format;
 import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.text.NumberFormat;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
+import java.text.AttributedCharacterIterator;
 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;
@@ -38,14 +39,18 @@ import org.apache.sis.util.resources.Err
 
 /**
  * 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:
+ * by the {@code parse} method is determined by the class of range elements:
  *
  * <ul>
- *   <li>If the components type is assignable to {@link Date}, then the {@code parse}
method
+ *   <li>If the elements 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>
+ *   <li>If the elements type is assignable to {@link Number}, then:
+ *     <ul>
+ *       <li>If the text to parse contains a {@linkplain Unit unit} of measure, then
+ *           the {@code parse} method will create {@link MeasurementRange} objects.</li>
+ *       <li>Otherwise the {@code parse} method will create {@link NumberRange} objects.</li>
+ *     </ul>
+ *   </li>
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -66,42 +71,81 @@ public class RangeFormat extends Format 
 
     /**
      * 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();
-     * }
+     * @see Field#MIN_VALUE
      */
-    public static final int MIN_VALUE_FIELD = 0;
-    // Note: the implementation in this class requires that MIN_VALUE_FIELD is 0.
+    private static final int MIN_VALUE_FIELD = 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();
-     * }
+     * @see Field#MAX_VALUE
      */
-    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.
+    private static final int MAX_VALUE_FIELD = 1;
 
     /**
      * The constant value for {@link FieldPosition} which designate the units of measurement.
-     * This field can <strong>not</strong> be combined with other field masks.
+     *
+     * @see Field#UNIT
      */
-    public static final int UNIT_FIELD = 0x20000000;
+    private static final int UNIT_FIELD = 2;
+
+    /**
+     * Defines constants that are used as attribute keys in the iterator returned from
+     * {@link RangeFormat#formatToCharacterIterator(Object)}.
+     *
+     * @author  Martin Desruisseaux (Geomatys)
+     * @since   0.3
+     * @version 0.3
+     * @module
+     */
+    public static final class Field extends FormatField {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 2286464612919602208L;
+
+        /**
+         * Creates a new field of the given name. The given name shall
+         * be identical to the name of the public static constant.
+         */
+        private Field(final String name, final int fieldID) {
+            super(name, fieldID);
+        }
+
+        /**
+         * Identifies the minimal value field in a range.
+         * When formatting a string, this value may be specified to the {@link FieldPosition}
+         * constructor in order to get the bounding index where the minimal value has been
written.
+         */
+        public static final Field MIN_VALUE = new Field("MIN_VALUE", MIN_VALUE_FIELD);
+
+        /**
+         * Identifies the maximal value field in a range.
+         * When formatting a string, this value may be specified to the {@link FieldPosition}
+         * constructor in order to get the bounding index where the maximal value has been
written.
+         */
+        public static final Field MAX_VALUE = new Field("MAX_VALUE", MAX_VALUE_FIELD);
+
+        /**
+         * Identifies the unit field in a range, if any.
+         * When formatting a string, this value may be specified to the {@link FieldPosition}
+         * constructor in order to get the bounding index where the unit has been written.
+         */
+        public static final Field UNIT = new Field("UNIT", UNIT_FIELD);
+
+        /**
+         * Returns the field constant for the given numeric identifier.
+         */
+        static Field forCode(final int field) {
+            switch (field) {
+                case MIN_VALUE_FIELD: return MIN_VALUE;
+                case MAX_VALUE_FIELD: return MAX_VALUE;
+                case UNIT_FIELD:      return UNIT;
+                default: throw new AssertionError(field);
+            }
+        }
+    }
 
     /**
      * The symbols used for parsing and formatting a range.
@@ -124,9 +168,11 @@ public class RangeFormat extends Format 
      * 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>
+     *   <li>{@link NumberRange} if the element type is assignable to {@link Number}
or {@link Angle}.</li>
+     *   <li>{@link DateRange}   if the element type is assignable to {@link Date}.</li>
      * </ul>
+     *
+     * @see Range#getElementType()
      */
     protected final Class<?> elementType;
 
@@ -135,9 +181,9 @@ public class RangeFormat extends Format 
      * 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>
+     *   <li>{@link AngleFormat}  if the element type is assignable to {@link Angle}.</li>
+     *   <li>{@link NumberFormat} if the element type is assignable to {@link Number}.</li>
+     *   <li>{@link DateFormat}   if the element type is assignable to {@link Date}.</li>
      * </ul>
      */
     protected final Format elementFormat;
@@ -149,25 +195,6 @@ public class RangeFormat extends Format 
     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}.
      */
@@ -256,6 +283,10 @@ public class RangeFormat extends Format 
      * @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.
+     *
+     * @see DecimalFormat#toPattern()
+     * @see SimpleDateFormat#toPattern()
+     * @see AngleFormat#toPattern()
      */
     public String getElementPattern(final boolean localized) {
         final Format format = elementFormat;
@@ -280,6 +311,10 @@ public class RangeFormat extends Format 
      * @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.
+     *
+     * @see DecimalFormat#applyPattern(String)
+     * @see SimpleDateFormat#applyPattern(String)
+     * @see AngleFormat#applyPattern(String)
      */
     public void setElementPattern(final String pattern, final boolean localized) {
         final Format format = elementFormat;
@@ -305,125 +340,208 @@ public class RangeFormat extends Format 
     }
 
     /**
-     * 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.
+     * Returns the {@code *_FIELD} constant for the given field position, or -1 if none.
+     */
+    private static int getField(final FieldPosition position) {
+        if (position != null) {
+            final Format.Field field = position.getFieldAttribute();
+            if (field instanceof Field) {
+                return ((Field) field).field;
+            }
+            return position.getField();
+        }
+        return -1;
+    }
+
+    /**
+     * Casts the given object to a {@code Range}, or throws an {@code IllegalArgumentException}
+     * if the given object is not a {@code Range} instance.
+     */
+    private static Range<?> cast(final Object range) throws IllegalArgumentException
{
+        if (range instanceof Range<?>) {
+            return (Range<?>) 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);
+    }
+
+    /**
+     * Formats a {@link Range} and appends the resulting text to a given string buffer.
+     * This method formats then given range as below:
+     *
+     * <ul>
+     *   <li>If the range is empty, then this method appends {@code "[]"}.</li>
+     *   <li>Otherwise if the minimal value is equals to the maximal value, then
+     *       that value is formatted alone.</li>
+     *   <li>Otherwise the minimal and maximal values are formatted like {@code [min
… max]} for
+     *       inclusive bounds or {@code (min … max)} for exclusive bounds, or a mix
of both styles.
+     *       The ∞ symbol is used in place of {@code min} or {@code max} for unbounded
ranges.</li>
+     * </ul>
+     *
+     * If the given range is a {@link MeasurementRange}, then the unit of measurement is
+     * appended to the above string representation.
      *
      * @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.
+     * @param pos        Identifies a field in the formatted text, or {@code null} if none.
      * @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);
-        }
+        format(cast(range), toAppendTo, pos, null);
+        return toAppendTo;
+    }
+
+    /**
+     * Implementation of the format methods.
+     *
+     * @param range      The range to format.
+     * @param toAppendTo Where the text is to be appended.
+     * @param pos        Identifies a field in the formatted text, or {@code null} if none.
+     * @param characterIterator The character iterator for which the attributes need to be
set,
+     *        or null if none. This is actually an instance of {@link FormattedCharacterIterator},
+     *        but we use the interface here for avoiding too early class loading.
+     */
+    @SuppressWarnings("fallthrough")
+    private void format(final Range<?> range, final StringBuffer toAppendTo, final
FieldPosition pos,
+            final AttributedCharacterIterator characterIterator)
+    {
         /*
          * 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()) {
+        int fieldPos = getField(pos);
+        if (range.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);
+            if (fieldPos >= MIN_VALUE_FIELD && fieldPos <= UNIT_FIELD) {
+                final int p = toAppendTo.length();
+                pos.setBeginIndex(p); // First index, inclusive.
+                pos.setEndIndex  (p); // Last index, exclusive
+            }
+            toAppendTo.append(s.closeInclusive);
+            return;
         }
         /*
-         * 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);
+         * Format a non-empty range by looping over all possible fields.
+         *
+         * Secial case: if minimal and maximal values are the same,
+         * formats only the maximal value.
+         */
+        final Comparable<?> minValue = range.getMinValue();
+        final Comparable<?> maxValue = range.getMaxValue();
+        final boolean isSingleton = (minValue != null) && minValue.equals(maxValue);
+        int field;
+        if (isSingleton) {
+            if (fieldPos == MIN_VALUE_FIELD) {
+                fieldPos = MAX_VALUE_FIELD;
+            }
+            field = MAX_VALUE_FIELD;
         } else {
-            minPos = new FieldPosition(fieldID & ~MAX_VALUE_FIELD);
-            maxPos = minPos; // Will overwrite the value of minPos.
+            field = MIN_VALUE_FIELD;
+            toAppendTo.append(range.isMinIncluded() ? s.openInclusive : s.openExclusive);
         }
-        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);
+        for (; field <= UNIT_FIELD; field++) {
+            final Object value;
+            switch (field) {
+                case MIN_VALUE_FIELD: value = minValue; break;
+                case MAX_VALUE_FIELD: value = maxValue; break;
+                case UNIT_FIELD:      value = range.getUnits(); break;
+                default: throw new AssertionError(field);
             }
-            toAppendTo.append(' ').append(s.separator).append(' ');
-            if (maxValue == null) {
-                maxPos.setBeginIndex(toAppendTo.length());
-                toAppendTo.append(infinity);
-                maxPos.setEndIndex(toAppendTo.length());
+            int startPosition = toAppendTo.length();
+            if (value == null) {
+                switch (field) {
+                    case MIN_VALUE_FIELD: toAppendTo.append(minusSign); // Fall through
+                    case MAX_VALUE_FIELD: toAppendTo.append(infinity); break;
+                }
             } else {
-                elementFormat.format(maxValue, toAppendTo, maxPos);
+                final Format format;
+                if (field == UNIT_FIELD) {
+                    startPosition = toAppendTo.append(' ').length();
+                    format = unitFormat;
+                } else {
+                    format = elementFormat;
+                }
+                if (characterIterator != null) {
+                    ((FormattedCharacterIterator) characterIterator)
+                            .append(format.formatToCharacterIterator(value), toAppendTo);
+                } else {
+                    format.format(value, toAppendTo, new FieldPosition(-1));
+                }
             }
-            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());
+            /*
+             * At this point, the field has been formatted. Now store the field index,
+             * then append the separator between this field and the next one.
+             */
+            if (characterIterator != null) {
+                ((FormattedCharacterIterator) characterIterator)
+                        .addFieldLimit(Field.forCode(field), value, startPosition);
+            }
+            if (field == fieldPos) {
+                pos.setBeginIndex(startPosition);
+                pos.setEndIndex(toAppendTo.length());
+            }
+            switch (field) {
+                case MIN_VALUE_FIELD: {
+                    toAppendTo.append(' ').append(s.separator).append(' ');
+                    break;
                 }
-                unitFormat.format(units, toAppendTo, pos);
-                if (isUnitField) {
-                    pos.setEndIndex(toAppendTo.length());
+                case MAX_VALUE_FIELD: {
+                    if (!isSingleton) {
+                        toAppendTo.append(range.isMaxIncluded() ? s.closeInclusive : s.closeExclusive);
+                    }
+                    break;
                 }
-                return toAppendTo;
             }
         }
-        if (isUnitField) {
-            final int length = toAppendTo.length();
-            pos.setBeginIndex(length);
-            pos.setEndIndex  (length);
-        }
-        return toAppendTo;
+    }
+
+    /**
+     * Formats a range as an attributed character iterator.
+     * Callers can iterate and queries the attribute values as in the following example:
+     *
+     * {@preformat java
+     *     AttributedCharacterIterator it = rangeFormat.formatToCharacterIterator(myRange);
+     *     for (char c=it.first(); c!=AttributedCharacterIterator.DONE; c=c.next()) {
+     *         // 'c' is a character from the formatted string.
+     *         if (it.getAttribute(RangeFormat.Field.MIN_VALUE) != null) {
+     *             // If we enter this block, then the character 'c' is part of the minimal
value,
+     *             // This field extends from it.getRunStart(MIN_VALUE) to it.getRunLimit(MIN_VALUE).
+     *         }
+     *     }
+     * }
+     *
+     * Alternatively, if the current {@linkplain AttributedCharacterIterator#getIndex() iterator
+     * index} is before the start of the minimum value field, then the starting position
of that
+     * field can be obtained directly by {@code it.getRunLimit(MIN_VALUE)}. If the current
iterator
+     * index is inside the minimum value field, then the above method call will rather returns
the
+     * end of that field. The same strategy works for other all fields too.
+     *
+     * <p>The returned character iterator contains all {@link java.text.NumberFormat.Field},
+     * {@link java.text.DateFormat.Field} or {@link org.apache.sis.measure.AngleFormat.Field}
+     * attributes in addition to the {@link Field} ones. Consequently the same character
may
+     * have more than one attribute.</p>
+     *
+     * <p>In Apache SIS implementation, the returned character iterator also implements
the
+     * {@link CharSequence} interface for convenience.</p>
+     *
+     * @param  range {@link Range} object to format.
+     * @return A character iterator together with the attributes describing the formatted
value.
+     * @throws IllegalArgumentException if {@code value} if not an instance of {@link Range}.
+     */
+    @Override
+    public AttributedCharacterIterator formatToCharacterIterator(final Object range) {
+        final StringBuffer buffer = new StringBuffer();
+        final FormattedCharacterIterator it = new FormattedCharacterIterator(buffer);
+        format(cast(range), buffer, null, it);
+        return it;
     }
 
     /**

Modified: sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/package-info.java?rev=1445183&r1=1445182&r2=1445183&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/package-info.java (original)
+++ sis/branches/JDK7/sis-utility/src/main/java/org/apache/sis/measure/package-info.java Tue
Feb 12 14:37:44 2013
@@ -24,6 +24,10 @@
  *   <li>{@link org.apache.sis.measure.Angle} and its subclasses
  *      ({@link org.apache.sis.measure.Longitude},
  *       {@link org.apache.sis.measure.Latitude})</li>
+ *   <li>{@link org.apache.sis.measure.Range} and its subclasses
+ *      ({@link org.apache.sis.measure.DateRange},
+ *       {@link org.apache.sis.measure.NumberRange},
+ *       {@link org.apache.sis.measure.MeasurementRange})</li>
  *   <li>Formatters
  *      ({@link org.apache.sis.measure.AngleFormat},
  *       {@link org.apache.sis.measure.CoordinateFormat},

Modified: 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=1445183&r1=1445182&r2=1445183&view=diff
==============================================================================
--- sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java
(original)
+++ sis/branches/JDK7/sis-utility/src/test/java/org/apache/sis/measure/RangeFormatTest.java
Tue Feb 12 14:37:44 2013
@@ -19,11 +19,12 @@ 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.FieldPosition;
 import java.text.ParsePosition;
 import java.text.ParseException;
+import java.text.AttributedCharacterIterator;
 import javax.measure.unit.SI;
 import org.junit.Test;
 import org.apache.sis.test.TestCase;
@@ -93,24 +94,24 @@ public final strictfp class RangeFormatT
     }
 
     /**
-     * Tests the {@link RangeFormat#format} method with numbers.
+     * Tests the {@link RangeFormat#format(Object, StringBuffer, FieldPosition)} method with
numbers.
      */
     @Test
-    public void testFormatNumber() {
+    public void testFormatNumbers() {
         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);
+        minPos = new FieldPosition(RangeFormat.Field.MIN_VALUE);
+        maxPos = new FieldPosition(RangeFormat.Field.MAX_VALUE);
 
         // Closed range
         assertEquals("[-10 … 20]", format(NumberRange.create(-10, 20)));
-        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.beginIndex", 1, 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.beginIndex", 1, minPos.getBeginIndex());
         assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
         assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
         assertEquals("maxPos.endIndex",   7, maxPos.getEndIndex());
@@ -145,7 +146,7 @@ public final strictfp class RangeFormatT
 
         // Negative infinity
         assertEquals("(-∞ … 30]", format(NumberRange.create(Double.NEGATIVE_INFINITY,
30)));
-        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
         assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
         assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
         assertEquals("maxPos.endIndex",   8, maxPos.getEndIndex());
@@ -159,7 +160,7 @@ public final strictfp class RangeFormatT
 
         // Positive infinities
         assertEquals("(-∞ … ∞)", format(NumberRange.create(Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY)));
-        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.beginIndex", 1, minPos.getBeginIndex());
         assertEquals("minPos.endIndex",   3, minPos.getEndIndex());
         assertEquals("maxPos.beginIndex", 6, maxPos.getBeginIndex());
         assertEquals("maxPos.endIndex",   7, maxPos.getEndIndex());
@@ -173,19 +174,19 @@ public final strictfp class RangeFormatT
 
         // Negative infinity with integers
         assertEquals("(-∞ … 40]", format(new NumberRange<>(Integer.class,
null, 40)));
-        assertEquals("minPos.beginIndex", 2, minPos.getBeginIndex());
+        assertEquals("minPos.beginIndex", 1, 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.beginIndex", 1, 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);
+        maxPos = new FieldPosition(RangeFormat.Field.UNIT);
         assertEquals("[-1 … 2] km", format(MeasurementRange.create(-1, 2, SI.KILOMETRE)));
         assertEquals("unitPos.beginIndex", 9, maxPos.getBeginIndex());
         assertEquals("unitPos.endIndex",  11, maxPos.getEndIndex());
@@ -197,7 +198,7 @@ public final strictfp class RangeFormatT
      * type itself.
      */
     @Test
-    public void testParseInteger() {
+    public void testParseIntegers() {
         format   = new RangeFormat(Locale.CANADA, Integer.class);
         parsePos = new ParsePosition(0);
 
@@ -217,7 +218,7 @@ public final strictfp class RangeFormatT
      * type itself.
      */
     @Test
-    public void testParseDouble() {
+    public void testParseDoubles() {
         format   = new RangeFormat(Locale.CANADA, Double.class);
         parsePos = new ParsePosition(0);
 
@@ -264,13 +265,26 @@ public final strictfp class RangeFormatT
     }
 
     /**
-     * Tests formatting and parsing with dates.
+     * Stores the field indices of the years in the {@link #minPos} and {@link #maxPos} fields.
+     */
+    private static void findYears(final AttributedCharacterIterator it,
+            final RangeFormat.Field field, final FieldPosition pos)
+    {
+        it.setIndex(it.getRunLimit(field));
+        it.setIndex(it.getRunLimit(DateFormat.Field.YEAR));
+        pos.setBeginIndex(it.getIndex());
+        it.setIndex(it.getRunLimit(DateFormat.Field.YEAR));
+        pos.setEndIndex(it.getIndex());
+    }
+
+    /**
+     * Tests the {@link RangeFormat#formatToCharacterIterator(Object)} method with dates.
      */
     @Test
-    public void testDateRange() {
+    public void testFormatDatesToCharacterIterator() {
         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);
+        minPos   = new FieldPosition(RangeFormat.Field.MIN_VALUE);
+        maxPos   = new FieldPosition(RangeFormat.Field.MAX_VALUE);
         parsePos = new ParsePosition(0);
 
         final long HOUR = 60L * 60 * 1000;
@@ -278,27 +292,35 @@ public final strictfp class RangeFormatT
         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);
+        AttributedCharacterIterator it = format.formatToCharacterIterator(range);
+        String text = it.toString();
+        findYears(it, RangeFormat.Field.MIN_VALUE, minPos);
+        findYears(it, RangeFormat.Field.MAX_VALUE, maxPos);
         assertEquals("[16/01/70 18:00 … 16/01/90 09:00]", text);
+        assertEquals( 7, minPos.getBeginIndex());
+        assertEquals( 9, minPos.getEndIndex());
+        assertEquals(24, maxPos.getBeginIndex());
+        assertEquals(26, maxPos.getEndIndex());
         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);
+        it    = format.formatToCharacterIterator(range);
+        text  = it.toString();
+        findYears(it, RangeFormat.Field.MAX_VALUE, maxPos);
         assertEquals("(-∞ … 01/01/90 00:00]", text);
+        assertEquals(12, maxPos.getBeginIndex());
+        assertEquals(14, maxPos.getEndIndex());
         assertEquals(range, parse(text));
 
         range = new DateRange(new Date(20*YEAR), (Date) null);
-        text  = format(range);
+        it    = format.formatToCharacterIterator(range);
+        text  = it.toString();
+        findYears(it, RangeFormat.Field.MIN_VALUE, minPos);
         assertEquals("[01/01/90 00:00 … ∞)", text);
+        assertEquals(7, minPos.getBeginIndex());
+        assertEquals(9, minPos.getEndIndex());
         assertEquals(range, parse(text));
     }
 }



Mime
View raw message