]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/fmtable.cpp
ICU-64232.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / fmtable.cpp
index cda867e6d555046dd63b1030aa6c0c600c0f804c..d813424494f8077e241f950cee5b0da7d83dcba7 100644 (file)
@@ -1,7 +1,9 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
-* Copyright (C) 1997-2001, International Business Machines Corporation and    *
-* others. All Rights Reserved.                                                *
+* Copyright (C) 1997-2016, International Business Machines Corporation and
+* others. All Rights Reserved.
 *******************************************************************************
 *
 * File FMTABLE.CPP
 
 #if !UCONFIG_NO_FORMATTING
 
+#include <cstdlib>
+#include <math.h>
 #include "unicode/fmtable.h"
+#include "unicode/ustring.h"
+#include "unicode/measure.h"
+#include "unicode/curramt.h"
+#include "unicode/uformattable.h"
+#include "charstr.h"
 #include "cmemory.h"
+#include "cstring.h"
+#include "fmtableimp.h"
+#include "number_decimalquantity.h"
 
 // *****************************************************************************
 // class Formattable
 
 U_NAMESPACE_BEGIN
 
-const char Formattable::fgClassID=0;
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Formattable)
+
+using number::impl::DecimalQuantity;
+
+
+//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
+
+// NOTE: As of 3.0, there are limitations to the UObject API.  It does
+// not (yet) support cloning, operator=, nor operator==.  To
+// work around this, I implement some simple inlines here.  Later
+// these can be modified or removed.  [alan]
+
+// NOTE: These inlines assume that all fObjects are in fact instances
+// of the Measure class, which is true as of 3.0.  [alan]
+
+// Return TRUE if *a == *b.
+static inline UBool objectEquals(const UObject* a, const UObject* b) {
+    // LATER: return *a == *b;
+    return *((const Measure*) a) == *((const Measure*) b);
+}
+
+// Return a clone of *a.
+static inline UObject* objectClone(const UObject* a) {
+    // LATER: return a->clone();
+    return ((const Measure*) a)->clone();
+}
+
+// Return TRUE if *a is an instance of Measure.
+static inline UBool instanceOfMeasure(const UObject* a) {
+    return dynamic_cast<const Measure*>(a) != NULL;
+}
+
+/**
+ * Creates a new Formattable array and copies the values from the specified
+ * original.
+ * @param array the original array
+ * @param count the original array count
+ * @return the new Formattable array.
+ */
+static Formattable* createArrayCopy(const Formattable* array, int32_t count) {
+    Formattable *result = new Formattable[count];
+    if (result != NULL) {
+        for (int32_t i=0; i<count; ++i)
+            result[i] = array[i]; // Don't memcpy!
+    }
+    return result;
+}
+
+//-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
+
+/**
+ * Set 'ec' to 'err' only if 'ec' is not already set to a failing UErrorCode.
+ */
+static void setError(UErrorCode& ec, UErrorCode err) {
+    if (U_SUCCESS(ec)) {
+        ec = err;
+    }
+}
+
+//
+//  Common initialization code, shared by constructors.
+//  Put everything into a known state.
+//
+void  Formattable::init() {
+    fValue.fInt64 = 0;
+    fType = kLong;
+    fDecimalStr = NULL;
+    fDecimalQuantity = NULL;
+    fBogus.setToBogus(); 
+}
 
 // -------------------------------------
 // default constructor.
 // Creates a formattable object with a long value 0.
 
-Formattable::Formattable()
-    :   UObject(), fType(kLong)
-{
-    fValue.fLong = 0;
+Formattable::Formattable() {
+    init();
 }
 
 // -------------------------------------
 // Creates a formattable object with a Date instance.
 
 Formattable::Formattable(UDate date, ISDATE /*isDate*/)
-    :   UObject(), fType(kDate)
 {
+    init();
+    fType = kDate;
     fValue.fDate = date;
 }
 
@@ -51,35 +131,47 @@ Formattable::Formattable(UDate date, ISDATE /*isDate*/)
 // Creates a formattable object with a double value.
 
 Formattable::Formattable(double value)
-    :   UObject(), fType(kDouble)
 {
+    init();
+    fType = kDouble;
     fValue.fDouble = value;
 }
 
 // -------------------------------------
-// Creates a formattable object with a long value.
+// Creates a formattable object with an int32_t value.
 
 Formattable::Formattable(int32_t value)
-    :   UObject(), fType(kLong)
 {
-    fValue.fLong = value;
+    init();
+    fValue.fInt64 = value;
 }
 
 // -------------------------------------
-// Creates a formattable object with a char* string.
+// Creates a formattable object with an int64_t value.
 
-Formattable::Formattable(const char* stringToCopy)
-    :   UObject(), fType(kString)
+Formattable::Formattable(int64_t value)
 {
-    fValue.fString = new UnicodeString(stringToCopy);
+    init();
+    fType = kInt64;
+    fValue.fInt64 = value;
 }
 
+// -------------------------------------
+// Creates a formattable object with a decimal number value from a string.
+
+Formattable::Formattable(StringPiece number, UErrorCode &status) {
+    init();
+    setDecimalNumber(number, status);
+}
+
+
 // -------------------------------------
 // Creates a formattable object with a UnicodeString instance.
 
 Formattable::Formattable(const UnicodeString& stringToCopy)
-    :   UObject(), fType(kString)
 {
+    init();
+    fType = kString;
     fValue.fString = new UnicodeString(stringToCopy);
 }
 
@@ -88,16 +180,26 @@ Formattable::Formattable(const UnicodeString& stringToCopy)
 // (adopting symantics)
 
 Formattable::Formattable(UnicodeString* stringToAdopt)
-    :   UObject(), fType(kString)
 {
+    init();
+    fType = kString;
     fValue.fString = stringToAdopt;
 }
 
+Formattable::Formattable(UObject* objectToAdopt)
+{
+    init();
+    fType = kObject;
+    fValue.fObject = objectToAdopt;
+}
+
 // -------------------------------------
 
 Formattable::Formattable(const Formattable* arrayToCopy, int32_t count)
     :   UObject(), fType(kArray)
 {
+    init();
+    fType = kArray;
     fValue.fArrayAndCount.fArray = createArrayCopy(arrayToCopy, count);
     fValue.fArrayAndCount.fCount = count;
 }
@@ -105,9 +207,11 @@ Formattable::Formattable(const Formattable* arrayToCopy, int32_t count)
 // -------------------------------------
 // copy constructor
 
+
 Formattable::Formattable(const Formattable &source)
-    :   UObject(source), fType(kLong)
+     :  UObject(*this)
 {
+    init();
     *this = source;
 }
 
@@ -141,13 +245,29 @@ Formattable::operator=(const Formattable& source)
             fValue.fDouble = source.fValue.fDouble;
             break;
         case kLong:
+        case kInt64:
             // Sets the long value.
-            fValue.fLong = source.fValue.fLong;
+            fValue.fInt64 = source.fValue.fInt64;
             break;
         case kDate:
             // Sets the Date value.
             fValue.fDate = source.fValue.fDate;
             break;
+        case kObject:
+            fValue.fObject = objectClone(source.fValue.fObject);
+            break;
+        }
+
+        UErrorCode status = U_ZERO_ERROR;
+        if (source.fDecimalQuantity != NULL) {
+          fDecimalQuantity = new DecimalQuantity(*source.fDecimalQuantity);
+        }
+        if (source.fDecimalStr != NULL) {
+            fDecimalStr = new CharString(*source.fDecimalStr, status);
+            if (U_FAILURE(status)) {
+                delete fDecimalStr;
+                fDecimalStr = NULL;
+            }
         }
     }
     return *this;
@@ -158,32 +278,53 @@ Formattable::operator=(const Formattable& source)
 UBool
 Formattable::operator==(const Formattable& that) const
 {
-    // Checks class ID.
+    int32_t i;
+
     if (this == &that) return TRUE;
 
     // Returns FALSE if the data types are different.
     if (fType != that.fType) return FALSE;
 
     // Compares the actual data values.
+    UBool equal = TRUE;
     switch (fType) {
     case kDate:
-        return fValue.fDate == that.fValue.fDate;
+        equal = (fValue.fDate == that.fValue.fDate);
+        break;
     case kDouble:
-        return fValue.fDouble == that.fValue.fDouble;
+        equal = (fValue.fDouble == that.fValue.fDouble);
+        break;
     case kLong:
-        return fValue.fLong == that.fValue.fLong;
+    case kInt64:
+        equal = (fValue.fInt64 == that.fValue.fInt64);
+        break;
     case kString:
-        return *(fValue.fString) == *(that.fValue.fString);
+        equal = (*(fValue.fString) == *(that.fValue.fString));
+        break;
     case kArray:
-        if (fValue.fArrayAndCount.fCount != that.fValue.fArrayAndCount.fCount)
-            return FALSE;
+        if (fValue.fArrayAndCount.fCount != that.fValue.fArrayAndCount.fCount) {
+            equal = FALSE;
+            break;
+        }
         // Checks each element for equality.
-        for (int32_t i=0; i<fValue.fArrayAndCount.fCount; ++i)
-            if (fValue.fArrayAndCount.fArray[i] != that.fValue.fArrayAndCount.fArray[i])
-                return FALSE;
+        for (i=0; i<fValue.fArrayAndCount.fCount; ++i) {
+            if (fValue.fArrayAndCount.fArray[i] != that.fValue.fArrayAndCount.fArray[i]) {
+                equal = FALSE;
+                break;
+            }
+        }
+        break;
+    case kObject:
+        if (fValue.fObject == NULL || that.fValue.fObject == NULL) {
+            equal = FALSE;
+        } else {
+            equal = objectEquals(fValue.fObject, that.fValue.fObject);
+        }
         break;
     }
-    return TRUE;
+
+    // TODO:  compare digit lists if numeric.
+    return equal;
 }
 
 // -------------------------------------
@@ -205,11 +346,26 @@ void Formattable::dispose()
     case kArray:
         delete[] fValue.fArrayAndCount.fArray;
         break;
-    case kDate:
-    case kDouble:
-    case kLong:
+    case kObject:
+        delete fValue.fObject;
+        break;
+    default:
         break;
     }
+
+    fType = kLong;
+    fValue.fInt64 = 0;
+
+    delete fDecimalStr;
+    fDecimalStr = NULL;
+
+    delete fDecimalQuantity;
+    fDecimalQuantity = NULL;
+}
+
+Formattable *
+Formattable::clone() const {
+    return new Formattable(*this);
 }
 
 // -------------------------------------
@@ -220,6 +376,155 @@ Formattable::getType() const
     return fType;
 }
 
+UBool
+Formattable::isNumeric() const {
+    switch (fType) {
+    case kDouble:
+    case kLong:
+    case kInt64:
+        return TRUE;
+    default:
+        return FALSE;
+    }
+}
+
+// -------------------------------------
+int32_t
+//Formattable::getLong(UErrorCode* status) const
+Formattable::getLong(UErrorCode& status) const
+{
+    if (U_FAILURE(status)) {
+        return 0;
+    }
+        
+    switch (fType) {
+    case Formattable::kLong: 
+        return (int32_t)fValue.fInt64;
+    case Formattable::kInt64:
+        if (fValue.fInt64 > INT32_MAX) {
+            status = U_INVALID_FORMAT_ERROR;
+            return INT32_MAX;
+        } else if (fValue.fInt64 < INT32_MIN) {
+            status = U_INVALID_FORMAT_ERROR;
+            return INT32_MIN;
+        } else {
+            return (int32_t)fValue.fInt64;
+        }
+    case Formattable::kDouble:
+        if (fValue.fDouble > INT32_MAX) {
+            status = U_INVALID_FORMAT_ERROR;
+            return INT32_MAX;
+        } else if (fValue.fDouble < INT32_MIN) {
+            status = U_INVALID_FORMAT_ERROR;
+            return INT32_MIN;
+        } else {
+            return (int32_t)fValue.fDouble; // loses fraction
+        }
+    case Formattable::kObject:
+        if (fValue.fObject == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return 0;
+        }
+        // TODO Later replace this with instanceof call
+        if (instanceOfMeasure(fValue.fObject)) {
+            return ((const Measure*) fValue.fObject)->
+                getNumber().getLong(status);
+        }
+        U_FALLTHROUGH;
+    default:
+        status = U_INVALID_FORMAT_ERROR;
+        return 0;
+    }
+}
+
+// -------------------------------------
+// Maximum int that can be represented exactly in a double.  (53 bits)
+//    Larger ints may be rounded to a near-by value as not all are representable.
+// TODO:  move this constant elsewhere, possibly configure it for different
+//        floating point formats, if any non-standard ones are still in use.
+static const int64_t U_DOUBLE_MAX_EXACT_INT = 9007199254740992LL;
+
+int64_t
+Formattable::getInt64(UErrorCode& status) const
+{
+    if (U_FAILURE(status)) {
+        return 0;
+    }
+        
+    switch (fType) {
+    case Formattable::kLong: 
+    case Formattable::kInt64: 
+        return fValue.fInt64;
+    case Formattable::kDouble:
+        if (fValue.fDouble > (double)U_INT64_MAX) {
+            status = U_INVALID_FORMAT_ERROR;
+            return U_INT64_MAX;
+        } else if (fValue.fDouble < (double)U_INT64_MIN) {
+            status = U_INVALID_FORMAT_ERROR;
+            return U_INT64_MIN;
+        } else if (fabs(fValue.fDouble) > U_DOUBLE_MAX_EXACT_INT && fDecimalQuantity != NULL) {
+            if (fDecimalQuantity->fitsInLong(true)) {
+                return fDecimalQuantity->toLong();
+            } else {
+                // Unexpected
+                status = U_INVALID_FORMAT_ERROR;
+                return fDecimalQuantity->isNegative() ? U_INT64_MIN : U_INT64_MAX;
+            }
+        } else {
+            return (int64_t)fValue.fDouble;
+        } 
+    case Formattable::kObject:
+        if (fValue.fObject == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return 0;
+        }
+        if (instanceOfMeasure(fValue.fObject)) {
+            return ((const Measure*) fValue.fObject)->
+                getNumber().getInt64(status);
+        }
+        U_FALLTHROUGH;
+    default:
+        status = U_INVALID_FORMAT_ERROR;
+        return 0;
+    }
+}
+
+// -------------------------------------
+double
+Formattable::getDouble(UErrorCode& status) const
+{
+    if (U_FAILURE(status)) {
+        return 0;
+    }
+        
+    switch (fType) {
+    case Formattable::kLong: 
+    case Formattable::kInt64: // loses precision
+        return (double)fValue.fInt64;
+    case Formattable::kDouble:
+        return fValue.fDouble;
+    case Formattable::kObject:
+        if (fValue.fObject == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return 0;
+        }
+        // TODO Later replace this with instanceof call
+        if (instanceOfMeasure(fValue.fObject)) {
+            return ((const Measure*) fValue.fObject)->
+                getNumber().getDouble(status);
+        }
+        U_FALLTHROUGH;
+    default:
+        status = U_INVALID_FORMAT_ERROR;
+        return 0;
+    }
+}
+
+const UObject*
+Formattable::getObject() const {
+    return (fType == kObject) ? fValue.fObject : NULL;
+}
+
 // -------------------------------------
 // Sets the value to a double value d.
 
@@ -239,7 +544,18 @@ Formattable::setLong(int32_t l)
 {
     dispose();
     fType = kLong;
-    fValue.fLong = l;
+    fValue.fInt64 = l;
+}
+
+// -------------------------------------
+// Sets the value to an int64 value ll.
+
+void
+Formattable::setInt64(int64_t ll)
+{
+    dispose();
+    fType = kInt64;
+    fValue.fInt64 = ll;
 }
 
 // -------------------------------------
@@ -299,18 +615,214 @@ Formattable::adoptArray(Formattable* array, int32_t count)
     fValue.fArrayAndCount.fCount = count;
 }
 
+void
+Formattable::adoptObject(UObject* objectToAdopt) {
+    dispose();
+    fType = kObject;
+    fValue.fObject = objectToAdopt;
+}
+
+// -------------------------------------
+UnicodeString& 
+Formattable::getString(UnicodeString& result, UErrorCode& status) const 
+{
+    if (fType != kString) {
+        setError(status, U_INVALID_FORMAT_ERROR);
+        result.setToBogus();
+    } else {
+        if (fValue.fString == NULL) {
+            setError(status, U_MEMORY_ALLOCATION_ERROR);
+        } else {
+            result = *fValue.fString;
+        }
+    }
+    return result;
+}
+
+// -------------------------------------
+const UnicodeString& 
+Formattable::getString(UErrorCode& status) const 
+{
+    if (fType != kString) {
+        setError(status, U_INVALID_FORMAT_ERROR);
+        return *getBogus();
+    }
+    if (fValue.fString == NULL) {
+        setError(status, U_MEMORY_ALLOCATION_ERROR);
+        return *getBogus();
+    }
+    return *fValue.fString;
+}
+
+// -------------------------------------
+UnicodeString& 
+Formattable::getString(UErrorCode& status) 
+{
+    if (fType != kString) {
+        setError(status, U_INVALID_FORMAT_ERROR);
+        return *getBogus();
+    }
+    if (fValue.fString == NULL) {
+       setError(status, U_MEMORY_ALLOCATION_ERROR);
+       return *getBogus();
+    }
+    return *fValue.fString;
+}
+
+// -------------------------------------
+const Formattable* 
+Formattable::getArray(int32_t& count, UErrorCode& status) const 
+{
+    if (fType != kArray) {
+        setError(status, U_INVALID_FORMAT_ERROR);
+        count = 0;
+        return NULL;
+    }
+    count = fValue.fArrayAndCount.fCount; 
+    return fValue.fArrayAndCount.fArray;
+}
+
+// -------------------------------------
+// Gets the bogus string, ensures mondo bogosity.
+
+UnicodeString*
+Formattable::getBogus() const 
+{
+    return (UnicodeString*)&fBogus; /* cast away const :-( */
+}
+
+
+// --------------------------------------
+StringPiece Formattable::getDecimalNumber(UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return "";
+    }
+    if (fDecimalStr != NULL) {
+      return fDecimalStr->toStringPiece();
+    }
+
+    CharString *decimalStr = internalGetCharString(status);
+    if(decimalStr == NULL) {
+      return ""; // getDecimalNumber returns "" for error cases
+    } else {
+      return decimalStr->toStringPiece();
+    }
+}
+
+CharString *Formattable::internalGetCharString(UErrorCode &status) {
+    if(fDecimalStr == NULL) {
+      if (fDecimalQuantity == NULL) {
+        // No decimal number for the formattable yet.  Which means the value was
+        // set directly by the user as an int, int64 or double.  If the value came
+        // from parsing, or from the user setting a decimal number, fDecimalNum
+        // would already be set.
+        //
+        LocalPointer<DecimalQuantity> dq(new DecimalQuantity(), status);
+        if (U_FAILURE(status)) { return nullptr; }
+        populateDecimalQuantity(*dq, status);
+        if (U_FAILURE(status)) { return nullptr; }
+        fDecimalQuantity = dq.orphan();
+      }
+
+      fDecimalStr = new CharString();
+      if (fDecimalStr == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+      }
+      // Older ICUs called uprv_decNumberToString here, which is not exactly the same as
+      // DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does
+      // not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?).
+      if (fDecimalQuantity->isInfinite()) {
+        fDecimalStr->append("Infinity", status);
+      } else if (fDecimalQuantity->isNaN()) {
+        fDecimalStr->append("NaN", status);
+      } else if (fDecimalQuantity->isZero()) {
+        fDecimalStr->append("0", -1, status);
+      } else if (fType==kLong || fType==kInt64 || // use toPlainString for integer types
+                  (fDecimalQuantity->getMagnitude() != INT32_MIN && std::abs(fDecimalQuantity->getMagnitude()) < 5)) {
+        fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status);
+      } else {
+        fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status);
+      }
+    }
+    return fDecimalStr;
+}
+
+void
+Formattable::populateDecimalQuantity(number::impl::DecimalQuantity& output, UErrorCode& status) const {
+    if (fDecimalQuantity != nullptr) {
+        output = *fDecimalQuantity;
+        return;
+    }
+
+    switch (fType) {
+        case kDouble:
+            output.setToDouble(this->getDouble());
+            output.roundToInfinity();
+            break;
+        case kLong:
+            output.setToInt(this->getLong());
+            break;
+        case kInt64:
+            output.setToLong(this->getInt64());
+            break;
+        default:
+            // The formattable's value is not a numeric type.
+            status = U_INVALID_STATE_ERROR;
+    }
+}
+
+// ---------------------------------------
+void
+Formattable::adoptDecimalQuantity(DecimalQuantity *dq) {
+    if (fDecimalQuantity != NULL) {
+        delete fDecimalQuantity;
+    }
+    fDecimalQuantity = dq;
+    if (dq == NULL) { // allow adoptDigitList(NULL) to clear
+        return;
+    }
+
+    // Set the value into the Union of simple type values.
+    // Cannot use the set() functions because they would delete the fDecimalNum value.
+    if (fDecimalQuantity->fitsInLong()) {
+        fValue.fInt64 = fDecimalQuantity->toLong();
+        if (fValue.fInt64 <= INT32_MAX && fValue.fInt64 >= INT32_MIN) {
+            fType = kLong;
+        } else {
+            fType = kInt64;
+        }
+    } else {
+        fType = kDouble;
+        fValue.fDouble = fDecimalQuantity->toDouble();
+    }
+}
+
+
+// ---------------------------------------
+void
+Formattable::setDecimalNumber(StringPiece numberString, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    dispose();
+
+    auto* dq = new DecimalQuantity();
+    dq->setToDecNumber(numberString, status);
+    adoptDecimalQuantity(dq);
+
+    // Note that we do not hang on to the caller's input string.
+    // If we are asked for the string, we will regenerate one from fDecimalQuantity.
+}
+
 #if 0
 //----------------------------------------------------
 // console I/O
 //----------------------------------------------------
 #ifdef _DEBUG
 
-#if U_IOSTREAM_SOURCE >= 199711
 #include <iostream>
 using namespace std;
-#elif U_IOSTREAM_SOURCE >= 198506
-#include <iostream.h>
-#endif
 
 #include "unicode/datefmt.h"
 #include "unistrm.h"
@@ -379,6 +891,152 @@ FormattableStreamer::streamOut(ostream& stream, const Formattable& obj)
 
 U_NAMESPACE_END
 
+/* ---- UFormattable implementation ---- */
+
+U_NAMESPACE_USE
+
+U_DRAFT UFormattable* U_EXPORT2
+ufmt_open(UErrorCode *status) {
+  if( U_FAILURE(*status) ) {
+    return NULL;
+  }
+  UFormattable *fmt = (new Formattable())->toUFormattable();
+
+  if( fmt == NULL ) {
+    *status = U_MEMORY_ALLOCATION_ERROR;
+  }
+  return fmt;
+}
+
+U_DRAFT void U_EXPORT2
+ufmt_close(UFormattable *fmt) {
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  delete obj;
+}
+
+U_INTERNAL UFormattableType U_EXPORT2
+ufmt_getType(const UFormattable *fmt, UErrorCode *status) {
+  if(U_FAILURE(*status)) {
+    return (UFormattableType)UFMT_COUNT;
+  }
+  const Formattable *obj = Formattable::fromUFormattable(fmt);
+  return (UFormattableType)obj->getType();
+}
+
+
+U_INTERNAL UBool U_EXPORT2
+ufmt_isNumeric(const UFormattable *fmt) {
+  const Formattable *obj = Formattable::fromUFormattable(fmt);
+  return obj->isNumeric();
+}
+
+U_DRAFT UDate U_EXPORT2
+ufmt_getDate(const UFormattable *fmt, UErrorCode *status) {
+  const Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  return obj->getDate(*status);
+}
+
+U_DRAFT double U_EXPORT2
+ufmt_getDouble(UFormattable *fmt, UErrorCode *status) {
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  return obj->getDouble(*status);
+}
+
+U_DRAFT int32_t U_EXPORT2
+ufmt_getLong(UFormattable *fmt, UErrorCode *status) {
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  return obj->getLong(*status);
+}
+
+
+U_DRAFT const void *U_EXPORT2
+ufmt_getObject(const UFormattable *fmt, UErrorCode *status) {
+  const Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  const void *ret = obj->getObject();
+  if( ret==NULL &&
+      (obj->getType() != Formattable::kObject) &&
+      U_SUCCESS( *status )) {
+    *status = U_INVALID_FORMAT_ERROR;
+  }
+  return ret;
+}
+
+U_DRAFT const UChar* U_EXPORT2
+ufmt_getUChars(UFormattable *fmt, int32_t *len, UErrorCode *status) {
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  // avoid bogosity by checking the type first.
+  if( obj->getType() != Formattable::kString ) {
+    if( U_SUCCESS(*status) ){
+      *status = U_INVALID_FORMAT_ERROR;
+    }
+    return NULL;
+  }
+
+  // This should return a valid string
+  UnicodeString &str = obj->getString(*status);
+  if( U_SUCCESS(*status) && len != NULL ) {
+    *len = str.length();
+  }
+  return str.getTerminatedBuffer();
+}
+
+U_DRAFT int32_t U_EXPORT2
+ufmt_getArrayLength(const UFormattable* fmt, UErrorCode *status) {
+  const Formattable *obj = Formattable::fromUFormattable(fmt);
+
+  int32_t count;
+  (void)obj->getArray(count, *status);
+  return count;
+}
+
+U_DRAFT UFormattable * U_EXPORT2
+ufmt_getArrayItemByIndex(UFormattable* fmt, int32_t n, UErrorCode *status) {
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+  int32_t count;
+  (void)obj->getArray(count, *status);
+  if(U_FAILURE(*status)) {
+    return NULL;
+  } else if(n<0 || n>=count) {
+    setError(*status, U_INDEX_OUTOFBOUNDS_ERROR);
+    return NULL;
+  } else {
+    return (*obj)[n].toUFormattable(); // returns non-const Formattable
+  }
+}
+
+U_DRAFT const char * U_EXPORT2
+ufmt_getDecNumChars(UFormattable *fmt, int32_t *len, UErrorCode *status) {
+  if(U_FAILURE(*status)) {
+    return "";
+  }
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+  CharString *charString = obj->internalGetCharString(*status);
+  if(U_FAILURE(*status)) {
+    return "";
+  }
+  if(charString == NULL) {
+    *status = U_MEMORY_ALLOCATION_ERROR;
+    return "";
+  } else {
+    if(len!=NULL) {
+      *len = charString->length();
+    }
+    return charString->data();
+  }
+}
+
+U_DRAFT int64_t U_EXPORT2
+ufmt_getInt64(UFormattable *fmt, UErrorCode *status) {
+  Formattable *obj = Formattable::fromUFormattable(fmt);
+  return obj->getInt64(*status);
+}
+
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
 //eof