ICU-11553 Add C wrapper for FieldPositionIterator & 2 udat_formatX calls to use it

X-SVN-Rev: 37118
This commit is contained in:
Peter Edberg 2015-03-04 00:11:53 +00:00
parent 1c9d614184
commit 50e67ce345
9 changed files with 464 additions and 39 deletions

2
.gitattributes vendored
View file

@ -88,6 +88,8 @@ icu4c/source/extra/uconv/uconv.vcxproj -text
icu4c/source/extra/uconv/uconv.vcxproj.filters -text
icu4c/source/i18n/i18n.vcxproj -text
icu4c/source/i18n/i18n.vcxproj.filters -text
icu4c/source/i18n/ufieldpositer.cpp -text
icu4c/source/i18n/unicode/ufieldpositer.h -text
icu4c/source/io/io.vcxproj -text
icu4c/source/io/io.vcxproj.filters -text
icu4c/source/layout/layout.vcxproj -text

View file

@ -1,6 +1,6 @@
#******************************************************************************
#
# Copyright (C) 1998-2014, International Business Machines
# Copyright (C) 1998-2015, International Business Machines
# Corporation and others. All Rights Reserved.
#
#******************************************************************************
@ -91,7 +91,7 @@ wintzimpl.o windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o v
upluralrules.o plurrule.o plurfmt.o selfmt.o dtitvfmt.o dtitvinf.o udateintervalformat.o \
tmunit.o tmutamt.o tmutfmt.o currpinf.o \
uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o smpdtfst.o \
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o ufieldpositer.o locdspnm.o \
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
uregion.o reldatefmt.o quantityformatter.o measunit.o filteredbrk.o \

View file

@ -365,6 +365,7 @@
<ClCompile Include="udat.cpp" />
<ClCompile Include="udateintervalformat.cpp" />
<ClCompile Include="udatpg.cpp" />
<ClCompile Include="ufieldpositer.cpp" />
<ClCompile Include="ulocdata.c" />
<ClCompile Include="umsg.cpp" />
<ClCompile Include="unum.cpp" />
@ -1421,6 +1422,20 @@
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
</CustomBuild>
<CustomBuild Include="unicode\ufieldpositer.h">
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">copy "%(FullPath)" ..\..\include\unicode
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">copy "%(FullPath)" ..\..\include\unicode
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">copy "%(FullPath)" ..\..\include\unicode
</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\..\include\unicode\%(Filename)%(Extension);%(Outputs)</Outputs>
</CustomBuild>

View file

@ -282,6 +282,9 @@
<ClCompile Include="udatpg.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="ufieldpositer.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="ulocdata.c">
<Filter>formatting</Filter>
</ClCompile>
@ -1134,6 +1137,9 @@
<CustomBuild Include="unicode\ugender.h">
<Filter>formatting</Filter>
</CustomBuild>
<CustomBuild Include="unicode\ufieldpositer.h">
<Filter>formatting</Filter>
</CustomBuild>
<CustomBuild Include="unicode\uldnames.h">
<Filter>formatting</Filter>
</CustomBuild>

View file

@ -22,6 +22,7 @@
#include "unicode/dtfmtsym.h"
#include "unicode/ustring.h"
#include "unicode/udisplaycontext.h"
#include "unicode/ufieldpositer.h"
#include "cpputils.h"
#include "reldtfmt.h"
#include "umutex.h"
@ -269,6 +270,50 @@ udat_formatCalendar(const UDateFormat* format,
return res.extract(result, resultLength, *status);
}
U_CAPI int32_t U_EXPORT2
udat_formatForFields( const UDateFormat* format,
UDate dateToFormat,
UChar* result,
int32_t resultLength,
UFieldPositionIterator* fpositer,
UErrorCode* status)
{
if(U_FAILURE(*status)) return -1;
UnicodeString res;
if(!(result==NULL && resultLength==0)) {
// NULL destination for pure preflighting: empty dummy string
// otherwise, alias the destination buffer
res.setTo(result, 0, resultLength);
}
((DateFormat*)format)->format(dateToFormat, res, (FieldPositionIterator*)fpositer, *status);
return res.extract(result, resultLength, *status);
}
U_CAPI int32_t U_EXPORT2
udat_formatCalendarForFields(const UDateFormat* format,
UCalendar* calendar,
UChar* result,
int32_t resultLength,
UFieldPositionIterator* fpositer,
UErrorCode* status)
{
if(U_FAILURE(*status)) return -1;
UnicodeString res;
if(!(result==NULL && resultLength==0)) {
// NULL destination for pure preflighting: empty dummy string
// otherwise, alias the destination buffer
res.setTo(result, 0, resultLength);
}
((DateFormat*)format)->format(*(Calendar*)calendar, res, (FieldPositionIterator*)fpositer, *status);
return res.extract(result, resultLength, *status);
}
U_CAPI UDate U_EXPORT2
udat_parse( const UDateFormat* format,
const UChar* text,

View file

@ -0,0 +1,59 @@
/*
*****************************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*****************************************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/ufieldpositer.h"
#include "unicode/fpositer.h"
#include "unicode/localpointer.h"
U_NAMESPACE_USE
U_CAPI UFieldPositionIterator* U_EXPORT2
ufieldpositer_open(UErrorCode* status)
{
if (U_FAILURE(*status)) {
return NULL;
}
FieldPositionIterator* fpositer = new FieldPositionIterator();
if (fpositer == NULL) {
*status = U_MEMORY_ALLOCATION_ERROR;
}
return (UFieldPositionIterator*)fpositer;
}
U_CAPI void U_EXPORT2
ufieldpositer_close(UFieldPositionIterator *fpositer)
{
delete (FieldPositionIterator*)fpositer;
}
U_CAPI int32_t U_EXPORT2
ufieldpositer_next(UFieldPositionIterator *fpositer,
int32_t *beginIndex, int32_t *endIndex)
{
FieldPosition fp;
int32_t field = -1;
if (((FieldPositionIterator*)fpositer)->next(fp)) {
field = fp.getField();
if (beginIndex) {
*beginIndex = fp.getBeginIndex();
}
if (endIndex) {
*endIndex = fp.getEndIndex();
}
}
return field;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -16,6 +16,7 @@
#include "unicode/ucal.h"
#include "unicode/unum.h"
#include "unicode/udisplaycontext.h"
#include "unicode/ufieldpositer.h"
/**
* \file
* \brief C API: DateFormat
@ -945,7 +946,7 @@ udat_clone(const UDateFormat *fmt,
UErrorCode *status);
/**
* Format a date using an UDateFormat.
* Format a date using a UDateFormat.
* The date will be formatted using the conventions specified in {@link #udat_open }
* @param format The formatter to use
* @param dateToFormat The date to format
@ -976,8 +977,8 @@ udat_format( const UDateFormat* format,
* The date will be formatted using the conventions specified in {@link #udat_open }
* @param format The formatter to use
* @param calendar The calendar to format. The calendar instance might be
* mutated when fields are not fully calculated yet, although
* this function won't change the logical date and time held
* mutated if fields are not yet fully calculated, though
* the function won't change the logical date and time held
* by the instance.
* @param result A pointer to a buffer to receive the formatted number.
* @param capacity The maximum size of result.
@ -1000,6 +1001,80 @@ udat_formatCalendar( const UDateFormat* format,
int32_t capacity,
UFieldPosition* position,
UErrorCode* status);
/**
* Format a date using a UDateFormat.
* The date will be formatted using the conventions specified in {@link #udat_open}
* @param format
* The formatter to use
* @param dateToFormat
* The date to format
* @param result
* A pointer to a buffer to receive the formatted number.
* @param resultLength
* The maximum size of result.
* @param fpositer
* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}
* (may be NULL if field position information is not needed). Any
* iteration information already present in the UFieldPositionIterator
* will be deleted, and the iterator will be reset to apply to the
* fields in the formatted string created by this function call; the
* field values provided by {@link #ufieldpositer_next} will be from the
* UDateFormatField enum.
* @param status
* A pointer to a UErrorCode to receive any errors
* @return
* The total buffer size needed; if greater than resultLength, the output was truncated.
* @see udat_parse
* @see UFieldPositionIterator
* @draft ICU 55
*/
U_DRAFT int32_t U_EXPORT2
udat_formatForFields( const UDateFormat* format,
UDate dateToFormat,
UChar* result,
int32_t resultLength,
UFieldPositionIterator* fpositer,
UErrorCode* status);
/**
* Format a date using a UDateFormat.
* The date will be formatted using the conventions specified in {@link #udat_open }
* @param format
* The formatter to use
* @param calendar
* The calendar to format. The calendar instance might be mutated if fields
* are not yet fully calculated, though the function won't change the logical
* date and time held by the instance.
* @param result
* A pointer to a buffer to receive the formatted number.
* @param capacity
* The maximum size of result.
* @param fpositer
* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}
* (may be NULL if field position information is not needed). Any
* iteration information already present in the UFieldPositionIterator
* will be deleted, and the iterator will be reset to apply to the
* fields in the formatted string created by this function call; the
* field values provided by {@link #ufieldpositer_next} will be from the
* UDateFormatField enum.
* @param status
* A pointer to a UErrorCode to receive any errors
* @return
* The total buffer size needed; if greater than resultLength, the output was truncated.
* @see udat_format
* @see udat_parseCalendar
* @see UFieldPositionIterator
* @draft ICU 55
*/
U_DRAFT int32_t U_EXPORT2
udat_formatCalendarForFields( const UDateFormat* format,
UCalendar* calendar,
UChar* result,
int32_t capacity,
UFieldPositionIterator* fpositer,
UErrorCode* status);
#endif /* U_HIDE_DRAFT_API */
/**
@ -1341,40 +1416,40 @@ typedef enum UDateFormatSymbolType {
UDAT_STANDALONE_SHORTER_WEEKDAYS
#ifndef U_HIDE_DRAFT_API
,
/**
* Cyclic year names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_CYCLIC_YEARS_WIDE)
* @draft ICU 54
*/
UDAT_CYCLIC_YEARS_WIDE,
/**
* Cyclic year names (only supported for some calendars, and only for FORMAT usage)
* @draft ICU 54
*/
UDAT_CYCLIC_YEARS_ABBREVIATED,
/**
* Cyclic year names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_CYCLIC_YEARS_NARROW)
* @draft ICU 54
*/
UDAT_CYCLIC_YEARS_NARROW,
/**
* Calendar zodiac names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_ZODIAC_NAMES_WIDE)
* @draft ICU 54
*/
UDAT_ZODIAC_NAMES_WIDE,
/**
* Calendar zodiac names (only supported for some calendars, and only for FORMAT usage)
* @draft ICU 54
*/
UDAT_ZODIAC_NAMES_ABBREVIATED,
/**
* Calendar zodiac names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_ZODIAC_NAMES_NARROW)
* @draft ICU 54
*/
UDAT_ZODIAC_NAMES_NARROW
/**
* Cyclic year names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_CYCLIC_YEARS_WIDE)
* @draft ICU 54
*/
UDAT_CYCLIC_YEARS_WIDE,
/**
* Cyclic year names (only supported for some calendars, and only for FORMAT usage)
* @draft ICU 54
*/
UDAT_CYCLIC_YEARS_ABBREVIATED,
/**
* Cyclic year names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_CYCLIC_YEARS_NARROW)
* @draft ICU 54
*/
UDAT_CYCLIC_YEARS_NARROW,
/**
* Calendar zodiac names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_ZODIAC_NAMES_WIDE)
* @draft ICU 54
*/
UDAT_ZODIAC_NAMES_WIDE,
/**
* Calendar zodiac names (only supported for some calendars, and only for FORMAT usage)
* @draft ICU 54
*/
UDAT_ZODIAC_NAMES_ABBREVIATED,
/**
* Calendar zodiac names (only supported for some calendars, and only for FORMAT usage;
* udat_setSymbols not supported for UDAT_ZODIAC_NAMES_NARROW)
* @draft ICU 54
*/
UDAT_ZODIAC_NAMES_NARROW
#endif /* U_HIDE_DRAFT_API */
} UDateFormatSymbolType;

View file

@ -0,0 +1,121 @@
/*
*****************************************************************************************
* Copyright (C) 2015, International Business Machines
* Corporation and others. All Rights Reserved.
*****************************************************************************************
*/
#ifndef UFIELDPOSITER_H
#define UFIELDPOSITER_H
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#ifndef U_HIDE_DRAFT_API
#include "unicode/localpointer.h"
/**
* \file
* \brief C API: UFieldPositionIterator for use with format APIs.
*
* Usage:
* ufieldpositer_open creates an empty (unset) UFieldPositionIterator.
* This can be passed to format functions such as {@link #udat_formatForFields},
* which will set it to apply to the fields in a particular formatted string.
* ufieldpositer_next can then be used to iterate over those fields,
* providing for each field its type (using values that are specific to the
* particular format type, such as date or number formats), as well as the
* start and end positions of the field in the formatted string.
* A given UFieldPositionIterator can be re-used for different format calls;
* each such call resets it to apply to that format string.
* ufieldpositer_close should be called to dispose of the UFieldPositionIterator
* when it is no longer needed.
*
* @see FieldPositionIterator
*/
/**
* Opaque UFieldPositionIterator object for use in C.
* @draft ICU 55
*/
struct UFieldPositionIterator;
typedef struct UFieldPositionIterator UFieldPositionIterator; /**< C typedef for struct UFieldPositionIterator. @draft ICU 55 */
/**
* Open a new, unset UFieldPositionIterator object.
* @param status
* A pointer to a UErrorCode to receive any errors.
* @return
* A pointer to an empty (unset) UFieldPositionIterator object,
* or NULL if an error occurred.
* @draft ICU 55
*/
U_DRAFT UFieldPositionIterator* U_EXPORT2
ufieldpositer_open(UErrorCode* status);
/**
* Close a UFieldPositionIterator object. Once closed it may no longer be used.
* @param fpositer
* A pointer to the UFieldPositionIterator object to close.
* @draft ICU 55
*/
U_DRAFT void U_EXPORT2
ufieldpositer_close(UFieldPositionIterator *fpositer);
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUFieldPositionIteratorPointer
* "Smart pointer" class, closes a UFieldPositionIterator via ufieldpositer_close().
* For most methods see the LocalPointerBase base class.
*
* @see LocalPointerBase
* @see LocalPointer
* @draft ICU 55
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUFieldPositionIteratorPointer, UFieldPositionIterator, ufieldpositer_close);
U_NAMESPACE_END
#endif
/**
* Get information for the next field in the formatted string to which this
* UFieldPositionIterator currently applies, or return FALSE if there are
* no more fields.
* @param fpositer
* A pointer to the UFieldPositionIterator object containing iteration
* state for the format fields.
* @param beginIndex
* A pointer to an int32_t to receive information about the start offset
* of the field in the formatted string (undefined if the function
* returns a negative value). May be NULL if this information is not needed.
* @param endIndex
* A pointer to an int32_t to receive information about the end offset
* of the field in the formatted string (undefined if the function
* returns a negative value). May be NULL if this information is not needed.
* @return
* The field type (non-negative value), or a negative value if there are
* no more fields for which to provide information. If negative, then any
* values pointed to by beginIndex and endIndex are undefined.
*
* The values for field type depend on what type of formatter the
* UFieldPositionIterator has been set by; for a date formatter, the
* values from the UDateFormatField enum. For more information, see the
* descriptions of format functions that take a UFieldPositionIterator*
* parameter, such as {@link #udat_formatForFields}.
*
* @draft ICU 55
*/
U_DRAFT int32_t U_EXPORT2
ufieldpositer_next(UFieldPositionIterator *fpositer,
int32_t *beginIndex, int32_t *endIndex);
#endif /* U_HIDE_DRAFT_API */
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif

View file

@ -25,6 +25,7 @@
#include "unicode/ucal.h"
#include "unicode/unum.h"
#include "unicode/ustring.h"
#include "unicode/ufieldpositer.h"
#include "cintltst.h"
#include "cdattst.h"
#include "cformtst.h"
@ -38,6 +39,7 @@ static void TestRelativeCrash(void);
static void TestContext(void);
static void TestCalendarDateParse(void);
static void TestParseErrorReturnValue(void);
static void TestFormatForFields(void);
#define LEN(a) (sizeof(a)/sizeof(a[0]))
@ -58,6 +60,7 @@ void addDateForTest(TestNode** root)
TESTCASE(TestCalendarDateParse);
TESTCASE(TestOverrideNumberFormat);
TESTCASE(TestParseErrorReturnValue);
TESTCASE(TestFormatForFields);
}
/* Testing the DateFormat API */
static void TestDateFormat()
@ -1734,4 +1737,103 @@ static void TestParseErrorReturnValue(void) {
udat_close(df);
}
/*
* Ticket #11553
* Test new udat_formatForFields, udat_formatCalendarForFields (and UFieldPositionIterator)
*/
static const char localeForFields[] = "en_US";
/* zoneGMT[]defined above */
static const UDate date2015Feb25 = 1424841000000.0; /* Wednesday, February 25, 2015 at 5:10:00 AM GMT */
typedef struct {
int32_t field;
int32_t beginPos;
int32_t endPos;
} FieldsData;
static const FieldsData expectedFields[] = {
{ UDAT_DAY_OF_WEEK_FIELD /* 9*/, 0, 9 },
{ UDAT_MONTH_FIELD /* 2*/, 11, 19 },
{ UDAT_DATE_FIELD /* 3*/, 20, 22 },
{ UDAT_YEAR_FIELD /* 1*/, 24, 28 },
{ UDAT_HOUR1_FIELD /*15*/, 32, 33 },
{ UDAT_TIME_SEPARATOR_FIELD /*35*/, 33, 34 },
{ UDAT_MINUTE_FIELD /* 6*/, 34, 36 },
{ UDAT_TIME_SEPARATOR_FIELD /*35*/, 36, 37 },
{ UDAT_SECOND_FIELD /* 7*/, 37, 39 },
{ UDAT_AM_PM_FIELD /*14*/, 40, 42 },
{ UDAT_TIMEZONE_FIELD /*17*/, 43, 46 },
{ -1, -1, -1 },
};
enum {kUBufFieldsLen = 128, kBBufFieldsLen = 256 };
static void TestFormatForFields(void) {
UErrorCode status = U_ZERO_ERROR;
UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
if ( U_FAILURE(status) ) {
log_err("ufieldpositer_open fails, status %s\n", u_errorName(status));
} else {
UDateFormat* udfmt = udat_open(UDAT_LONG, UDAT_FULL, localeForFields, zoneGMT, -1, NULL, 0, &status);
UCalendar* ucal = ucal_open(zoneGMT, -1, localeForFields, UCAL_DEFAULT, &status);
if ( U_FAILURE(status) ) {
log_data_err("udat_open or ucal_open fails for locale %s, status %s (Are you missing data?)\n", localeForFields, u_errorName(status));
} else {
int32_t ulen, field, beginPos, endPos;
UChar ubuf[kUBufFieldsLen];
const FieldsData * fptr;
status = U_ZERO_ERROR;
ulen = udat_formatForFields(udfmt, date2015Feb25, ubuf, kUBufFieldsLen, fpositer, &status);
if ( U_FAILURE(status) ) {
log_err("udat_formatForFields fails, status %s\n", u_errorName(status));
} else {
for (fptr = expectedFields; ; fptr++) {
field = ufieldpositer_next(fpositer, &beginPos, &endPos);
if (field != fptr->field || (field >= 0 && (beginPos != fptr->beginPos || endPos != fptr->endPos))) {
if (fptr->field >= 0) {
log_err("udat_formatForFields as \"%s\"; expect field %d range %d-%d, get field %d range %d-%d\n",
aescstrdup(ubuf, ulen), fptr->field, fptr->beginPos, fptr->endPos, field, beginPos, endPos);
} else {
log_err("udat_formatForFields as \"%s\"; expect field < 0, get field %d range %d-%d\n",
aescstrdup(ubuf, ulen), field, beginPos, endPos);
}
break;
}
if (field < 0) {
break;
}
}
}
ucal_setMillis(ucal, date2015Feb25, &status);
status = U_ZERO_ERROR;
ulen = udat_formatCalendarForFields(udfmt, ucal, ubuf, kUBufFieldsLen, fpositer, &status);
if ( U_FAILURE(status) ) {
log_err("udat_formatCalendarForFields fails, status %s\n", u_errorName(status));
} else {
for (fptr = expectedFields; ; fptr++) {
field = ufieldpositer_next(fpositer, &beginPos, &endPos);
if (field != fptr->field || (field >= 0 && (beginPos != fptr->beginPos || endPos != fptr->endPos))) {
if (fptr->field >= 0) {
log_err("udat_formatFudat_formatCalendarForFieldsorFields as \"%s\"; expect field %d range %d-%d, get field %d range %d-%d\n",
aescstrdup(ubuf, ulen), fptr->field, fptr->beginPos, fptr->endPos, field, beginPos, endPos);
} else {
log_err("udat_formatCalendarForFields as \"%s\"; expect field < 0, get field %d range %d-%d\n",
aescstrdup(ubuf, ulen), field, beginPos, endPos);
}
break;
}
if (field < 0) {
break;
}
}
}
ucal_close(ucal);
udat_close(udfmt);
}
ufieldpositer_close(fpositer);
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */