1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/dcfmtsym.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,459 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 1997-2013, International Business Machines Corporation and 1.7 +* others. All Rights Reserved. 1.8 +******************************************************************************* 1.9 +* 1.10 +* File DCFMTSYM.CPP 1.11 +* 1.12 +* Modification History: 1.13 +* 1.14 +* Date Name Description 1.15 +* 02/19/97 aliu Converted from java. 1.16 +* 03/18/97 clhuang Implemented with C++ APIs. 1.17 +* 03/27/97 helena Updated to pass the simple test after code review. 1.18 +* 08/26/97 aliu Added currency/intl currency symbol support. 1.19 +* 07/20/98 stephen Slightly modified initialization of monetarySeparator 1.20 +******************************************************************************** 1.21 +*/ 1.22 + 1.23 +#include "unicode/utypes.h" 1.24 + 1.25 +#if !UCONFIG_NO_FORMATTING 1.26 + 1.27 +#include "unicode/dcfmtsym.h" 1.28 +#include "unicode/ures.h" 1.29 +#include "unicode/decimfmt.h" 1.30 +#include "unicode/ucurr.h" 1.31 +#include "unicode/choicfmt.h" 1.32 +#include "unicode/unistr.h" 1.33 +#include "unicode/numsys.h" 1.34 +#include "unicode/unum.h" 1.35 +#include "unicode/utf16.h" 1.36 +#include "ucurrimp.h" 1.37 +#include "cstring.h" 1.38 +#include "locbased.h" 1.39 +#include "uresimp.h" 1.40 +#include "ureslocs.h" 1.41 + 1.42 +// ***************************************************************************** 1.43 +// class DecimalFormatSymbols 1.44 +// ***************************************************************************** 1.45 + 1.46 +U_NAMESPACE_BEGIN 1.47 + 1.48 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormatSymbols) 1.49 + 1.50 +static const char gNumberElements[] = "NumberElements"; 1.51 +static const char gCurrencySpacingTag[] = "currencySpacing"; 1.52 +static const char gBeforeCurrencyTag[] = "beforeCurrency"; 1.53 +static const char gAfterCurrencyTag[] = "afterCurrency"; 1.54 +static const char gCurrencyMatchTag[] = "currencyMatch"; 1.55 +static const char gCurrencySudMatchTag[] = "surroundingMatch"; 1.56 +static const char gCurrencyInsertBtnTag[] = "insertBetween"; 1.57 + 1.58 + 1.59 +static const UChar INTL_CURRENCY_SYMBOL_STR[] = {0xa4, 0xa4, 0}; 1.60 + 1.61 +// ------------------------------------- 1.62 +// Initializes this with the decimal format symbols in the default locale. 1.63 + 1.64 +DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status) 1.65 + : UObject(), 1.66 + locale() 1.67 +{ 1.68 + initialize(locale, status, TRUE); 1.69 +} 1.70 + 1.71 +// ------------------------------------- 1.72 +// Initializes this with the decimal format symbols in the desired locale. 1.73 + 1.74 +DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status) 1.75 + : UObject(), 1.76 + locale(loc) 1.77 +{ 1.78 + initialize(locale, status); 1.79 +} 1.80 + 1.81 +DecimalFormatSymbols::DecimalFormatSymbols() 1.82 + : UObject(), 1.83 + locale(Locale::getRoot()), 1.84 + currPattern(NULL) { 1.85 + *validLocale = *actualLocale = 0; 1.86 + initialize(); 1.87 +} 1.88 + 1.89 +DecimalFormatSymbols* 1.90 +DecimalFormatSymbols::createWithLastResortData(UErrorCode& status) { 1.91 + if (U_FAILURE(status)) { return NULL; } 1.92 + DecimalFormatSymbols* sym = new DecimalFormatSymbols(); 1.93 + if (sym == NULL) { 1.94 + status = U_MEMORY_ALLOCATION_ERROR; 1.95 + } 1.96 + return sym; 1.97 +} 1.98 + 1.99 +// ------------------------------------- 1.100 + 1.101 +DecimalFormatSymbols::~DecimalFormatSymbols() 1.102 +{ 1.103 +} 1.104 + 1.105 +// ------------------------------------- 1.106 +// copy constructor 1.107 + 1.108 +DecimalFormatSymbols::DecimalFormatSymbols(const DecimalFormatSymbols &source) 1.109 + : UObject(source) 1.110 +{ 1.111 + *this = source; 1.112 +} 1.113 + 1.114 +// ------------------------------------- 1.115 +// assignment operator 1.116 + 1.117 +DecimalFormatSymbols& 1.118 +DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs) 1.119 +{ 1.120 + if (this != &rhs) { 1.121 + for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { 1.122 + // fastCopyFrom is safe, see docs on fSymbols 1.123 + fSymbols[(ENumberFormatSymbol)i].fastCopyFrom(rhs.fSymbols[(ENumberFormatSymbol)i]); 1.124 + } 1.125 + for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { 1.126 + currencySpcBeforeSym[i].fastCopyFrom(rhs.currencySpcBeforeSym[i]); 1.127 + currencySpcAfterSym[i].fastCopyFrom(rhs.currencySpcAfterSym[i]); 1.128 + } 1.129 + locale = rhs.locale; 1.130 + uprv_strcpy(validLocale, rhs.validLocale); 1.131 + uprv_strcpy(actualLocale, rhs.actualLocale); 1.132 + } 1.133 + return *this; 1.134 +} 1.135 + 1.136 +// ------------------------------------- 1.137 + 1.138 +UBool 1.139 +DecimalFormatSymbols::operator==(const DecimalFormatSymbols& that) const 1.140 +{ 1.141 + if (this == &that) { 1.142 + return TRUE; 1.143 + } 1.144 + for(int32_t i = 0; i < (int32_t)kFormatSymbolCount; ++i) { 1.145 + if(fSymbols[(ENumberFormatSymbol)i] != that.fSymbols[(ENumberFormatSymbol)i]) { 1.146 + return FALSE; 1.147 + } 1.148 + } 1.149 + for(int32_t i = 0; i < (int32_t)UNUM_CURRENCY_SPACING_COUNT; ++i) { 1.150 + if(currencySpcBeforeSym[i] != that.currencySpcBeforeSym[i]) { 1.151 + return FALSE; 1.152 + } 1.153 + if(currencySpcAfterSym[i] != that.currencySpcAfterSym[i]) { 1.154 + return FALSE; 1.155 + } 1.156 + } 1.157 + return locale == that.locale && 1.158 + uprv_strcmp(validLocale, that.validLocale) == 0 && 1.159 + uprv_strcmp(actualLocale, that.actualLocale) == 0; 1.160 +} 1.161 + 1.162 +// ------------------------------------- 1.163 + 1.164 +void 1.165 +DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status, UBool useLastResortData) 1.166 +{ 1.167 + static const char *gNumberElementKeys[kFormatSymbolCount] = { 1.168 + "decimal", 1.169 + "group", 1.170 + "list", 1.171 + "percentSign", 1.172 + NULL, /* Native zero digit is deprecated from CLDR - get it from the numbering system */ 1.173 + NULL, /* Pattern digit character is deprecated from CLDR - use # by default always */ 1.174 + "minusSign", 1.175 + "plusSign", 1.176 + NULL, /* currency symbol - We don't really try to load this directly from CLDR until we know the currency */ 1.177 + NULL, /* intl currency symbol - We don't really try to load this directly from CLDR until we know the currency */ 1.178 + "currencyDecimal", 1.179 + "exponential", 1.180 + "perMille", 1.181 + NULL, /* Escape padding character - not in CLDR */ 1.182 + "infinity", 1.183 + "nan", 1.184 + NULL, /* Significant digit symbol - not in CLDR */ 1.185 + "currencyGroup", 1.186 + NULL, /* one digit - get it from the numbering system */ 1.187 + NULL, /* two digit - get it from the numbering system */ 1.188 + NULL, /* three digit - get it from the numbering system */ 1.189 + NULL, /* four digit - get it from the numbering system */ 1.190 + NULL, /* five digit - get it from the numbering system */ 1.191 + NULL, /* six digit - get it from the numbering system */ 1.192 + NULL, /* seven digit - get it from the numbering system */ 1.193 + NULL, /* eight digit - get it from the numbering system */ 1.194 + NULL, /* nine digit - get it from the numbering system */ 1.195 + }; 1.196 + 1.197 + static const char *gLatn = "latn"; 1.198 + static const char *gSymbols = "symbols"; 1.199 + const char *nsName; 1.200 + const UChar *sym = NULL; 1.201 + int32_t len = 0; 1.202 + 1.203 + *validLocale = *actualLocale = 0; 1.204 + currPattern = NULL; 1.205 + if (U_FAILURE(status)) 1.206 + return; 1.207 + 1.208 + const char* locStr = loc.getName(); 1.209 + LocalUResourceBundlePointer resource(ures_open(NULL, locStr, &status)); 1.210 + LocalUResourceBundlePointer numberElementsRes( 1.211 + ures_getByKeyWithFallback(resource.getAlias(), gNumberElements, NULL, &status)); 1.212 + 1.213 + if (U_FAILURE(status)) { 1.214 + if ( useLastResortData ) { 1.215 + status = U_USING_DEFAULT_WARNING; 1.216 + initialize(); 1.217 + } 1.218 + return; 1.219 + } 1.220 + 1.221 + // First initialize all the symbols to the fallbacks for anything we can't find 1.222 + initialize(); 1.223 + 1.224 + // 1.225 + // Next get the numbering system for this locale and set zero digit 1.226 + // and the digit string based on the numbering system for the locale 1.227 + // 1.228 + 1.229 + LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status)); 1.230 + if (U_SUCCESS(status) && ns->getRadix() == 10 && !ns->isAlgorithmic()) { 1.231 + nsName = ns->getName(); 1.232 + UnicodeString digitString(ns->getDescription()); 1.233 + int32_t digitIndex = 0; 1.234 + UChar32 digit = digitString.char32At(0); 1.235 + fSymbols[kZeroDigitSymbol].setTo(digit); 1.236 + for (int32_t i = kOneDigitSymbol; i <= kNineDigitSymbol; ++i) { 1.237 + digitIndex += U16_LENGTH(digit); 1.238 + digit = digitString.char32At(digitIndex); 1.239 + fSymbols[i].setTo(digit); 1.240 + } 1.241 + } else { 1.242 + nsName = gLatn; 1.243 + } 1.244 + 1.245 + UBool isLatn = !uprv_strcmp(nsName,gLatn); 1.246 + 1.247 + UErrorCode nlStatus = U_ZERO_ERROR; 1.248 + LocalUResourceBundlePointer nonLatnSymbols; 1.249 + if ( !isLatn ) { 1.250 + nonLatnSymbols.adoptInstead( 1.251 + ures_getByKeyWithFallback(numberElementsRes.getAlias(), nsName, NULL, &nlStatus)); 1.252 + ures_getByKeyWithFallback(nonLatnSymbols.getAlias(), gSymbols, nonLatnSymbols.getAlias(), &nlStatus); 1.253 + } 1.254 + 1.255 + LocalUResourceBundlePointer latnSymbols( 1.256 + ures_getByKeyWithFallback(numberElementsRes.getAlias(), gLatn, NULL, &status)); 1.257 + ures_getByKeyWithFallback(latnSymbols.getAlias(), gSymbols, latnSymbols.getAlias(), &status); 1.258 + 1.259 + UBool kMonetaryDecimalSet = FALSE; 1.260 + UBool kMonetaryGroupingSet = FALSE; 1.261 + for(int32_t i = 0; i<kFormatSymbolCount; i++) { 1.262 + if ( gNumberElementKeys[i] != NULL ) { 1.263 + UErrorCode localStatus = U_ZERO_ERROR; 1.264 + if ( !isLatn ) { 1.265 + sym = ures_getStringByKeyWithFallback(nonLatnSymbols.getAlias(), 1.266 + gNumberElementKeys[i], &len, &localStatus); 1.267 + // If we can't find the symbol in the numbering system specific resources, 1.268 + // use the "latn" numbering system as the fallback. 1.269 + if ( U_FAILURE(localStatus) ) { 1.270 + localStatus = U_ZERO_ERROR; 1.271 + sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(), 1.272 + gNumberElementKeys[i], &len, &localStatus); 1.273 + } 1.274 + } else { 1.275 + sym = ures_getStringByKeyWithFallback(latnSymbols.getAlias(), 1.276 + gNumberElementKeys[i], &len, &localStatus); 1.277 + } 1.278 + 1.279 + if ( U_SUCCESS(localStatus) ) { 1.280 + setSymbol((ENumberFormatSymbol)i, UnicodeString(TRUE, sym, len)); 1.281 + if ( i == kMonetarySeparatorSymbol ) { 1.282 + kMonetaryDecimalSet = TRUE; 1.283 + } else if ( i == kMonetaryGroupingSeparatorSymbol ) { 1.284 + kMonetaryGroupingSet = TRUE; 1.285 + } 1.286 + } 1.287 + } 1.288 + } 1.289 + 1.290 + // If monetary decimal or grouping were not explicitly set, then set them to be the 1.291 + // same as their non-monetary counterparts. 1.292 + 1.293 + if ( !kMonetaryDecimalSet ) { 1.294 + setSymbol(kMonetarySeparatorSymbol,fSymbols[kDecimalSeparatorSymbol]); 1.295 + } 1.296 + if ( !kMonetaryGroupingSet ) { 1.297 + setSymbol(kMonetaryGroupingSeparatorSymbol,fSymbols[kGroupingSeparatorSymbol]); 1.298 + } 1.299 + 1.300 + // Obtain currency data from the currency API. This is strictly 1.301 + // for backward compatibility; we don't use DecimalFormatSymbols 1.302 + // for currency data anymore. 1.303 + UErrorCode internalStatus = U_ZERO_ERROR; // don't propagate failures out 1.304 + UChar curriso[4]; 1.305 + UnicodeString tempStr; 1.306 + ucurr_forLocale(locStr, curriso, 4, &internalStatus); 1.307 + 1.308 + uprv_getStaticCurrencyName(curriso, locStr, tempStr, internalStatus); 1.309 + if (U_SUCCESS(internalStatus)) { 1.310 + fSymbols[kIntlCurrencySymbol].setTo(curriso, -1); 1.311 + fSymbols[kCurrencySymbol] = tempStr; 1.312 + } 1.313 + /* else use the default values. */ 1.314 + 1.315 + U_LOCALE_BASED(locBased, *this); 1.316 + locBased.setLocaleIDs(ures_getLocaleByType(numberElementsRes.getAlias(), 1.317 + ULOC_VALID_LOCALE, &status), 1.318 + ures_getLocaleByType(numberElementsRes.getAlias(), 1.319 + ULOC_ACTUAL_LOCALE, &status)); 1.320 + 1.321 + //load the currency data 1.322 + UChar ucc[4]={0}; //Currency Codes are always 3 chars long 1.323 + int32_t uccLen = 4; 1.324 + const char* locName = loc.getName(); 1.325 + UErrorCode localStatus = U_ZERO_ERROR; 1.326 + uccLen = ucurr_forLocale(locName, ucc, uccLen, &localStatus); 1.327 + 1.328 + if(U_SUCCESS(localStatus) && uccLen > 0) { 1.329 + char cc[4]={0}; 1.330 + u_UCharsToChars(ucc, cc, uccLen); 1.331 + /* An explicit currency was requested */ 1.332 + LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus)); 1.333 + LocalUResourceBundlePointer currency( 1.334 + ures_getByKeyWithFallback(currencyResource.getAlias(), "Currencies", NULL, &localStatus)); 1.335 + ures_getByKeyWithFallback(currency.getAlias(), cc, currency.getAlias(), &localStatus); 1.336 + if(U_SUCCESS(localStatus) && ures_getSize(currency.getAlias())>2) { // the length is 3 if more data is present 1.337 + ures_getByIndex(currency.getAlias(), 2, currency.getAlias(), &localStatus); 1.338 + int32_t currPatternLen = 0; 1.339 + currPattern = 1.340 + ures_getStringByIndex(currency.getAlias(), (int32_t)0, &currPatternLen, &localStatus); 1.341 + UnicodeString decimalSep = 1.342 + ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)1, &localStatus); 1.343 + UnicodeString groupingSep = 1.344 + ures_getUnicodeStringByIndex(currency.getAlias(), (int32_t)2, &localStatus); 1.345 + if(U_SUCCESS(localStatus)){ 1.346 + fSymbols[kMonetaryGroupingSeparatorSymbol] = groupingSep; 1.347 + fSymbols[kMonetarySeparatorSymbol] = decimalSep; 1.348 + //pattern.setTo(TRUE, currPattern, currPatternLen); 1.349 + status = localStatus; 1.350 + } 1.351 + } 1.352 + /* else An explicit currency was requested and is unknown or locale data is malformed. */ 1.353 + /* ucurr_* API will get the correct value later on. */ 1.354 + } 1.355 + // else ignore the error if no currency 1.356 + 1.357 + // Currency Spacing. 1.358 + localStatus = U_ZERO_ERROR; 1.359 + LocalUResourceBundlePointer currencyResource(ures_open(U_ICUDATA_CURR, locStr, &localStatus)); 1.360 + LocalUResourceBundlePointer currencySpcRes( 1.361 + ures_getByKeyWithFallback(currencyResource.getAlias(), 1.362 + gCurrencySpacingTag, NULL, &localStatus)); 1.363 + 1.364 + if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 1.365 + const char* keywords[UNUM_CURRENCY_SPACING_COUNT] = { 1.366 + gCurrencyMatchTag, gCurrencySudMatchTag, gCurrencyInsertBtnTag 1.367 + }; 1.368 + localStatus = U_ZERO_ERROR; 1.369 + LocalUResourceBundlePointer dataRes( 1.370 + ures_getByKeyWithFallback(currencySpcRes.getAlias(), 1.371 + gBeforeCurrencyTag, NULL, &localStatus)); 1.372 + if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 1.373 + localStatus = U_ZERO_ERROR; 1.374 + for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { 1.375 + currencySpcBeforeSym[i] = 1.376 + ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus); 1.377 + } 1.378 + } 1.379 + dataRes.adoptInstead( 1.380 + ures_getByKeyWithFallback(currencySpcRes.getAlias(), 1.381 + gAfterCurrencyTag, NULL, &localStatus)); 1.382 + if (localStatus == U_USING_FALLBACK_WARNING || U_SUCCESS(localStatus)) { 1.383 + localStatus = U_ZERO_ERROR; 1.384 + for (int32_t i = 0; i < UNUM_CURRENCY_SPACING_COUNT; i++) { 1.385 + currencySpcAfterSym[i] = 1.386 + ures_getUnicodeStringByKey(dataRes.getAlias(), keywords[i], &localStatus); 1.387 + } 1.388 + } 1.389 + } 1.390 +} 1.391 + 1.392 +void 1.393 +DecimalFormatSymbols::initialize() { 1.394 + /* 1.395 + * These strings used to be in static arrays, but the HP/UX aCC compiler 1.396 + * cannot initialize a static array with class constructors. 1.397 + * markus 2000may25 1.398 + */ 1.399 + fSymbols[kDecimalSeparatorSymbol] = (UChar)0x2e; // '.' decimal separator 1.400 + fSymbols[kGroupingSeparatorSymbol].remove(); // group (thousands) separator 1.401 + fSymbols[kPatternSeparatorSymbol] = (UChar)0x3b; // ';' pattern separator 1.402 + fSymbols[kPercentSymbol] = (UChar)0x25; // '%' percent sign 1.403 + fSymbols[kZeroDigitSymbol] = (UChar)0x30; // '0' native 0 digit 1.404 + fSymbols[kOneDigitSymbol] = (UChar)0x31; // '1' native 1 digit 1.405 + fSymbols[kTwoDigitSymbol] = (UChar)0x32; // '2' native 2 digit 1.406 + fSymbols[kThreeDigitSymbol] = (UChar)0x33; // '3' native 3 digit 1.407 + fSymbols[kFourDigitSymbol] = (UChar)0x34; // '4' native 4 digit 1.408 + fSymbols[kFiveDigitSymbol] = (UChar)0x35; // '5' native 5 digit 1.409 + fSymbols[kSixDigitSymbol] = (UChar)0x36; // '6' native 6 digit 1.410 + fSymbols[kSevenDigitSymbol] = (UChar)0x37; // '7' native 7 digit 1.411 + fSymbols[kEightDigitSymbol] = (UChar)0x38; // '8' native 8 digit 1.412 + fSymbols[kNineDigitSymbol] = (UChar)0x39; // '9' native 9 digit 1.413 + fSymbols[kDigitSymbol] = (UChar)0x23; // '#' pattern digit 1.414 + fSymbols[kPlusSignSymbol] = (UChar)0x002b; // '+' plus sign 1.415 + fSymbols[kMinusSignSymbol] = (UChar)0x2d; // '-' minus sign 1.416 + fSymbols[kCurrencySymbol] = (UChar)0xa4; // 'OX' currency symbol 1.417 + fSymbols[kIntlCurrencySymbol].setTo(TRUE, INTL_CURRENCY_SYMBOL_STR, 2); 1.418 + fSymbols[kMonetarySeparatorSymbol] = (UChar)0x2e; // '.' monetary decimal separator 1.419 + fSymbols[kExponentialSymbol] = (UChar)0x45; // 'E' exponential 1.420 + fSymbols[kPerMillSymbol] = (UChar)0x2030; // '%o' per mill 1.421 + fSymbols[kPadEscapeSymbol] = (UChar)0x2a; // '*' pad escape symbol 1.422 + fSymbols[kInfinitySymbol] = (UChar)0x221e; // 'oo' infinite 1.423 + fSymbols[kNaNSymbol] = (UChar)0xfffd; // SUB NaN 1.424 + fSymbols[kSignificantDigitSymbol] = (UChar)0x0040; // '@' significant digit 1.425 + fSymbols[kMonetaryGroupingSeparatorSymbol].remove(); // 1.426 +} 1.427 + 1.428 +Locale 1.429 +DecimalFormatSymbols::getLocale(ULocDataLocaleType type, UErrorCode& status) const { 1.430 + U_LOCALE_BASED(locBased, *this); 1.431 + return locBased.getLocale(type, status); 1.432 +} 1.433 + 1.434 +const UnicodeString& 1.435 +DecimalFormatSymbols::getPatternForCurrencySpacing(UCurrencySpacing type, 1.436 + UBool beforeCurrency, 1.437 + UErrorCode& status) const { 1.438 + if (U_FAILURE(status)) { 1.439 + return fNoSymbol; // always empty. 1.440 + } 1.441 + if (beforeCurrency) { 1.442 + return currencySpcBeforeSym[(int32_t)type]; 1.443 + } else { 1.444 + return currencySpcAfterSym[(int32_t)type]; 1.445 + } 1.446 +} 1.447 + 1.448 +void 1.449 +DecimalFormatSymbols::setPatternForCurrencySpacing(UCurrencySpacing type, 1.450 + UBool beforeCurrency, 1.451 + const UnicodeString& pattern) { 1.452 + if (beforeCurrency) { 1.453 + currencySpcBeforeSym[(int32_t)type] = pattern; 1.454 + } else { 1.455 + currencySpcAfterSym[(int32_t)type] = pattern; 1.456 + } 1.457 +} 1.458 +U_NAMESPACE_END 1.459 + 1.460 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.461 + 1.462 +//eof