ICU-20138 Adding a tech-preview API, constrainFieldAndValue, and updating matchhesField.
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
index 0380b0e..60a5467 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
@@ -601,7 +601,7 @@
continue;
}
// Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
- if (cfpos.matchesField(NumberFormat.Field.INTEGER)
+ if (cfpos.matchesField(NumberFormat.Field.INTEGER, null)
&& i > zero
// don't return the same field twice in a row:
&& i - zero > cfpos.getLimit()
@@ -614,7 +614,7 @@
}
// Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
if (numericField != null
- && cfpos.matchesField(numericField)
+ && cfpos.matchesField(numericField, null)
&& i > zero
// don't return the same field twice in a row:
&& (i - zero > cfpos.getLimit() || cfpos.getField() != numericField)
@@ -634,7 +634,7 @@
continue;
}
// Case 3: check for field starting at this position
- if (cfpos.matchesField(_field)) {
+ if (cfpos.matchesField(_field, null)) {
fieldStart = i - zero;
currField = _field;
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java b/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
index 20d3641..a4aa0eb 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
@@ -3,6 +3,7 @@
package com.ibm.icu.text;
import java.text.Format.Field;
+import java.util.Objects;
/**
* Represents a span of a string containing a given field.
@@ -51,7 +52,17 @@
*
* FormattedValue implementations should not change the field when this constraint is active.
*/
- FIELD
+ FIELD,
+
+ /**
+ * Represents that the field value is constrained.
+ *
+ * This is the value of fConstraint
+ * after {@link #constrainField} is called.
+ *
+ * FormattedValue implementations should not change the field or value with this constraint.
+ */
+ VALUE
};
private ConstraintType fConstraint;
@@ -97,7 +108,7 @@
* Sets a constraint on the field.
*
* When this instance of ConstrainedFieldPosition is passed to {@link FormattedValue#nextPosition}, positions are
- * skipped unless they have the given category and field.
+ * skipped unless they have the given field.
*
* Any previously set constraints are cleared.
*
@@ -126,6 +137,7 @@
fConstraint = ConstraintType.FIELD;
fClassConstraint = Object.class;
fField = field;
+ fValue = null;
}
/**
@@ -158,6 +170,36 @@
fConstraint = ConstraintType.CLASS;
fClassConstraint = classConstraint;
fField = null;
+ fValue = null;
+ }
+
+ /**
+ * Sets a constraint on field and field value.
+ *
+ * When this instance of ConstrainedFieldPosition is passed to {@link FormattedValue#nextPosition}, positions are
+ * skipped unless both the field and the field value are equal.
+ *
+ * Any previously set constraints are cleared.
+ *
+ * For example, to find the span a date interval corresponding to the first date:
+ *
+ * <pre>
+ * ConstrainedFieldPosition cfpos;
+ * cfpos.constrainFieldAndValue(DateIntervalFormat.SpanField.DATE_INTERVAL_SPAN, 0);
+ * while (fmtval.nextPosition(cfpos)) {
+ * // handle the span of the first date in the date interval
+ * }
+ * </pre>
+ *
+ * @param field The field to fix when iterating.
+ * @param fieldValue The field value to fix when iterating.
+ * @internal ICU 64 Technical Preview
+ */
+ public void constrainFieldAndValue(Field field, Object fieldValue) {
+ fConstraint = ConstraintType.VALUE;
+ fClassConstraint = Object.class;
+ fField = field;
+ fValue = fieldValue;
}
/**
@@ -253,7 +295,7 @@
* @param field
* The new field.
* @param value
- * The new field value.
+ * The new field value. Should be null if there is no value.
* @param start
* The new inclusive start index.
* @param limit
@@ -263,7 +305,7 @@
*/
public void setState(Field field, Object value, int start, int limit) {
// Check matchesField only as an assertion (debug build)
- assert matchesField(field);
+ assert matchesField(field, value);
fField = field;
fValue = value;
@@ -272,17 +314,18 @@
}
/**
- * Determines whether a given field should be included given the
+ * Determines whether a given field and value should be included given the
* constraints.
*
* Intended to be used by FormattedValue implementations.
*
* @param field The field to test.
+ * @param fieldValue The field value to test. Should be null if there is no value.
* @return Whether the field should be included given the constraints.
* @draft ICU 64
* @provisional This API might change or be removed in a future release.
*/
- public boolean matchesField(Field field) {
+ public boolean matchesField(Field field, Object fieldValue) {
if (field == null) {
throw new IllegalArgumentException("field must not be null");
}
@@ -293,6 +336,9 @@
return fClassConstraint.isAssignableFrom(field.getClass());
case FIELD:
return fField == field;
+ case VALUE:
+ // Note: Objects.equals is Android API level 19 and Java 1.7
+ return fField == field && Objects.equals(fValue, fieldValue);
default:
throw new AssertionError();
}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java
index dda4bf4..7aea483 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java
@@ -31,7 +31,7 @@
assertAllPartsEqual(
"basic",
cfpos,
- 7,
+ 15,
null,
null,
0,
@@ -47,18 +47,29 @@
assertAllPartsEqual(
"setters 1",
cfpos,
- 2,
+ 10,
NumberFormat.Field.COMPACT,
null,
0,
0,
0L);
+ cfpos.constrainFieldAndValue(NumberFormat.Field.COMPACT, 42);
+ assertAllPartsEqual(
+ "setters 1.2",
+ cfpos,
+ 8,
+ NumberFormat.Field.COMPACT,
+ 42,
+ 0,
+ 0,
+ 0L);
+
cfpos.constrainClass(NumberFormat.Field.class);
assertAllPartsEqual(
"setters 1.5",
cfpos,
- 3,
+ 11,
null,
null,
0,
@@ -69,7 +80,7 @@
assertAllPartsEqual(
"setters 2",
cfpos,
- 3,
+ 11,
null,
null,
0,
@@ -80,7 +91,7 @@
assertAllPartsEqual(
"setters 3",
cfpos,
- 3,
+ 11,
NumberFormat.Field.COMPACT,
BigDecimal.ONE,
5,
@@ -91,7 +102,7 @@
assertAllPartsEqual(
"setters 4",
cfpos,
- 7,
+ 15,
null,
null,
0,
@@ -103,7 +114,7 @@
public void testIllegalArgumentException() {
ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition();
try {
- cfpos.matchesField(null);
+ cfpos.matchesField(null, null);
fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
// pass
@@ -126,11 +137,13 @@
assertEquals(messagePrefix + ": context", context, cfpos.getInt64IterationContext());
assertEquals(messagePrefix + ": integer field",
- ((matching & 1) != 0), cfpos.matchesField(NumberFormat.Field.INTEGER));
+ ((matching & 1) != 0), cfpos.matchesField(NumberFormat.Field.INTEGER, null));
assertEquals(messagePrefix + ": compact field",
- ((matching & 2) != 0), cfpos.matchesField(NumberFormat.Field.COMPACT));
+ ((matching & 2) != 0), cfpos.matchesField(NumberFormat.Field.COMPACT, null));
assertEquals(messagePrefix + ": date field",
- ((matching & 4) != 0), cfpos.matchesField(DateFormat.Field.AM_PM));
+ ((matching & 4) != 0), cfpos.matchesField(DateFormat.Field.AM_PM, null));
+ assertEquals(messagePrefix + ": compact field with value",
+ ((matching & 8) != 0), cfpos.matchesField(NumberFormat.Field.COMPACT, 42));
}
public static void checkFormattedValue(String message, FormattedValue fv, String expectedString,