1/*******************************************************************************
2* Copyright (C) 2008-2010, International Business Machines Corporation and
3* others. All Rights Reserved.
4*******************************************************************************
5*
6* File DTITVINF.CPP
7*
8*******************************************************************************
9*/
10
11#include "unicode/dtitvinf.h"
12
13
14#if !UCONFIG_NO_FORMATTING
15
16//TODO: define it in compiler time
17//#define DTITVINF_DEBUG 1
18
19
20#ifdef DTITVINF_DEBUG
21#include <iostream>
22#endif
23
24#include "cstring.h"
25#include "unicode/msgfmt.h"
26#include "unicode/uloc.h"
27#include "unicode/ures.h"
28#include "dtitv_impl.h"
29#include "hash.h"
30#include "gregoimp.h"
31#include "uresimp.h"
32#include "hash.h"
33#include "gregoimp.h"
34#include "uresimp.h"
35
36
37U_NAMESPACE_BEGIN
38
39
40#ifdef DTITVINF_DEBUG
41#define PRINTMESG(msg) { std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; }
42#endif
43
44UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
45
46static const char gCalendarTag[]="calendar";
47static const char gGregorianTag[]="gregorian";
48static const char gIntervalDateTimePatternTag[]="intervalFormats";
49static const char gFallbackPatternTag[]="fallback";
50
51// {0}
52static const UChar gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
53// {1}
54static const UChar gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
55
56// default fall-back
57static const UChar gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0};
58
59
60
61DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
62:   fFallbackIntervalPattern(gDefaultFallbackPattern),
63    fFirstDateInPtnIsLaterDate(false),
64    fIntervalPatterns(NULL)
65{
66    fIntervalPatterns = initHash(status);
67}
68
69
70
71DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
72:   fFallbackIntervalPattern(gDefaultFallbackPattern),
73    fFirstDateInPtnIsLaterDate(false),
74    fIntervalPatterns(NULL)
75{
76    initializeData(locale, status);
77}
78
79
80
81void
82DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
83                                     UCalendarDateFields lrgDiffCalUnit,
84                                     const UnicodeString& intervalPattern,
85                                     UErrorCode& status) {
86
87    if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) {
88        setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status);
89        setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status);
90    } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH ||
91                lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) {
92        setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status);
93    } else {
94        setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
95    }
96}
97
98
99void
100DateIntervalInfo::setFallbackIntervalPattern(
101                                    const UnicodeString& fallbackPattern,
102                                    UErrorCode& status) {
103    if ( U_FAILURE(status) ) {
104        return;
105    }
106    int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
107                        sizeof(gFirstPattern)/sizeof(gFirstPattern[0]), 0);
108    int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
109                        sizeof(gSecondPattern)/sizeof(gSecondPattern[0]), 0);
110    if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
111        status = U_ILLEGAL_ARGUMENT_ERROR;
112        return;
113    }
114    if ( firstPatternIndex > secondPatternIndex ) {
115        fFirstDateInPtnIsLaterDate = true;
116    }
117    fFallbackIntervalPattern = fallbackPattern;
118}
119
120
121
122DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
123:   UObject(dtitvinf),
124    fIntervalPatterns(NULL)
125{
126    *this = dtitvinf;
127}
128
129
130
131DateIntervalInfo&
132DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
133    if ( this == &dtitvinf ) {
134        return *this;
135    }
136
137    UErrorCode status = U_ZERO_ERROR;
138    deleteHash(fIntervalPatterns);
139    fIntervalPatterns = initHash(status);
140    copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
141    if ( U_FAILURE(status) ) {
142        return *this;
143    }
144
145    fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
146    fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
147    return *this;
148}
149
150
151DateIntervalInfo*
152DateIntervalInfo::clone() const {
153    return new DateIntervalInfo(*this);
154}
155
156
157DateIntervalInfo::~DateIntervalInfo() {
158    deleteHash(fIntervalPatterns);
159    fIntervalPatterns = NULL;
160}
161
162
163UBool
164DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
165    UBool equal = (
166      fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
167      fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
168
169    if ( equal == TRUE ) {
170        equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
171    }
172
173    return equal;
174}
175
176
177UnicodeString&
178DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
179                                     UCalendarDateFields field,
180                                     UnicodeString& result,
181                                     UErrorCode& status) const {
182    if ( U_FAILURE(status) ) {
183        return result;
184    }
185
186    const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
187    if ( patternsOfOneSkeleton != NULL ) {
188        IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
189        if ( U_FAILURE(status) ) {
190            return result;
191        }
192        const UnicodeString& intervalPattern =  patternsOfOneSkeleton[index];
193        if ( !intervalPattern.isEmpty() ) {
194            result = intervalPattern;
195        }
196    }
197    return result;
198}
199
200
201UBool
202DateIntervalInfo::getDefaultOrder() const {
203    return fFirstDateInPtnIsLaterDate;
204}
205
206
207UnicodeString&
208DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
209    result = fFallbackIntervalPattern;
210    return result;
211}
212
213#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
214
215void
216DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& err)
217{
218  fIntervalPatterns = initHash(err);
219  if ( U_FAILURE(err) ) {
220      return;
221  }
222  const char *locName = locale.getName();
223  char parentLocale[ULOC_FULLNAME_CAPACITY];
224  int32_t locNameLen;
225  uprv_strcpy(parentLocale, locName);
226  UErrorCode status = U_ZERO_ERROR;
227  Hashtable skeletonSet(FALSE, status);
228  if ( U_FAILURE(status) ) {
229      return;
230  }
231
232  // determine calendar type
233  const char * calendarTypeToUse = gGregorianTag; // initial default
234  char         calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
235  char         localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
236  // obtain a locale that always has the calendar key value that should be used
237  (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL,
238                                     "calendar", "calendar", locName, NULL, FALSE, &status);
239  localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
240  // now get the calendar key value from that locale
241  int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, ULOC_KEYWORDS_CAPACITY, &status);
242  if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
243    calendarTypeToUse = calendarType;
244  }
245  status = U_ZERO_ERROR;
246
247  do {
248    UResourceBundle *rb, *calBundle, *calTypeBundle, *itvDtPtnResource;
249    rb = ures_open(NULL, parentLocale, &status);
250    calBundle = ures_getByKey(rb, gCalendarTag, NULL, &status);
251    calTypeBundle = ures_getByKey(calBundle, calendarTypeToUse, NULL, &status);
252    itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
253                         gIntervalDateTimePatternTag, NULL, &status);
254
255    if ( U_SUCCESS(status) ) {
256        // look for fallback first, since it establishes the default order
257        const UChar* resStr;
258        int32_t resStrLen = 0;
259        resStr = ures_getStringByKeyWithFallback(itvDtPtnResource,
260                                             gFallbackPatternTag,
261                                             &resStrLen, &status);
262        if ( U_SUCCESS(status) ) {
263            UnicodeString pattern = UnicodeString(TRUE, resStr, resStrLen);
264            setFallbackIntervalPattern(pattern, status);
265        }
266
267        int32_t size = ures_getSize(itvDtPtnResource);
268        int32_t index;
269        for ( index = 0; index < size; ++index ) {
270            UResourceBundle* oneRes = ures_getByIndex(itvDtPtnResource, index,
271                                                     NULL, &status);
272            if ( U_SUCCESS(status) ) {
273                const char* skeleton = ures_getKey(oneRes);
274                if ( skeleton == NULL ||
275                     skeletonSet.geti(UnicodeString(skeleton)) == 1 ) {
276                    ures_close(oneRes);
277                    continue;
278                }
279                skeletonSet.puti(UnicodeString(skeleton), 1, status);
280                if ( uprv_strcmp(skeleton, gFallbackPatternTag) == 0 ) {
281                    ures_close(oneRes);
282                    continue;  // fallback
283                }
284
285                UResourceBundle* intervalPatterns = ures_getByKey(
286                                     itvDtPtnResource, skeleton, NULL, &status);
287
288                if ( U_FAILURE(status) ) {
289                    ures_close(intervalPatterns);
290                    ures_close(oneRes);
291                    break;
292                }
293                if ( intervalPatterns == NULL ) {
294                    ures_close(intervalPatterns);
295                    ures_close(oneRes);
296                    continue;
297                }
298
299                const UChar* pattern;
300                const char* key;
301                int32_t ptLength;
302                int32_t ptnNum = ures_getSize(intervalPatterns);
303                int32_t ptnIndex;
304                for ( ptnIndex = 0; ptnIndex < ptnNum; ++ptnIndex ) {
305                    pattern = ures_getNextString(intervalPatterns, &ptLength, &key,
306                                                 &status);
307                    if ( U_FAILURE(status) ) {
308                        break;
309                    }
310
311                    UCalendarDateFields calendarField = UCAL_FIELD_COUNT;
312                    if ( !uprv_strcmp(key, "y") ) {
313                        calendarField = UCAL_YEAR;
314                    } else if ( !uprv_strcmp(key, "M") ) {
315                        calendarField = UCAL_MONTH;
316                    } else if ( !uprv_strcmp(key, "d") ) {
317                        calendarField = UCAL_DATE;
318                    } else if ( !uprv_strcmp(key, "a") ) {
319                        calendarField = UCAL_AM_PM;
320                    } else if ( !uprv_strcmp(key, "h") || !uprv_strcmp(key, "H") ) {
321                        calendarField = UCAL_HOUR;
322                    } else if ( !uprv_strcmp(key, "m") ) {
323                        calendarField = UCAL_MINUTE;
324                    }
325                    if ( calendarField != UCAL_FIELD_COUNT ) {
326                        setIntervalPatternInternally(skeleton, calendarField, pattern,status);
327                    }
328                }
329                ures_close(intervalPatterns);
330            }
331            ures_close(oneRes);
332        }
333    }
334    ures_close(itvDtPtnResource);
335    ures_close(calTypeBundle);
336    ures_close(calBundle);
337    ures_close(rb);
338    status = U_ZERO_ERROR;
339    locNameLen = uloc_getParent(parentLocale, parentLocale,
340                                ULOC_FULLNAME_CAPACITY,&status);
341  } while ( locNameLen > 0 );
342}
343
344
345
346void
347DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
348                                      UCalendarDateFields lrgDiffCalUnit,
349                                      const UnicodeString& intervalPattern,
350                                      UErrorCode& status) {
351    IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
352    if ( U_FAILURE(status) ) {
353        return;
354    }
355    UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
356    UBool emptyHash = false;
357    if ( patternsOfOneSkeleton == NULL ) {
358        patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
359        emptyHash = true;
360    }
361
362    patternsOfOneSkeleton[index] = intervalPattern;
363    if ( emptyHash == TRUE ) {
364        fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
365    }
366}
367
368
369
370void
371DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
372                                int32_t* skeletonFieldWidth) {
373    const int8_t PATTERN_CHAR_BASE = 0x41;
374    int32_t i;
375    for ( i = 0; i < skeleton.length(); ++i ) {
376        // it is an ASCII char in skeleton
377        int8_t ch = (int8_t)skeleton.charAt(i);
378        ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
379    }
380}
381
382
383
384UBool
385DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
386                                char patternLetter) {
387    if ( patternLetter == 'M' ) {
388        if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
389             (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
390            return true;
391        }
392    }
393    return false;
394}
395
396
397
398const UnicodeString*
399DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
400                                  int8_t& bestMatchDistanceInfo) const {
401#ifdef DTITVINF_DEBUG
402    char result[1000];
403    char result_1[1000];
404    char mesg[2000];
405    skeleton.extract(0,  skeleton.length(), result, "UTF-8");
406    sprintf(mesg, "in getBestSkeleton: skeleton: %s; \n", result);
407    PRINTMESG(mesg)
408#endif
409
410
411    int32_t inputSkeletonFieldWidth[] =
412    {
413    //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
414             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
415    //   P   Q   R   S   T   U   V   W   X   Y   Z
416         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
417    //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
418         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
419    //   p   q   r   s   t   u   v   w   x   y   z
420         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
421    };
422
423    int32_t skeletonFieldWidth[] =
424    {
425    //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
426             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
427    //   P   Q   R   S   T   U   V   W   X   Y   Z
428         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 0, 0,  0, 0, 0,
429    //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
430         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
431    //   p   q   r   s   t   u   v   w   x   y   z
432         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
433    };
434
435    const int32_t DIFFERENT_FIELD = 0x1000;
436    const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
437    const int32_t BASE = 0x41;
438    const UChar CHAR_V = 0x0076;
439    const UChar CHAR_Z = 0x007A;
440
441    // hack for 'v' and 'z'.
442    // resource bundle only have time skeletons ending with 'v',
443    // but not for time skeletons ending with 'z'.
444    UBool replaceZWithV = false;
445    const UnicodeString* inputSkeleton = &skeleton;
446    UnicodeString copySkeleton;
447    if ( skeleton.indexOf(CHAR_Z) != -1 ) {
448        UChar zstr[2];
449        UChar vstr[2];
450        zstr[0]=CHAR_Z;
451        vstr[0]=CHAR_V;
452        zstr[1]=0;
453        vstr[1]=0;
454        copySkeleton = skeleton;
455        copySkeleton.findAndReplace(zstr, vstr);
456        inputSkeleton = &copySkeleton;
457        replaceZWithV = true;
458    }
459
460    parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
461    int32_t bestDistance = MAX_POSITIVE_INT;
462    const UnicodeString* bestSkeleton = NULL;
463
464    // 0 means exact the same skeletons;
465    // 1 means having the same field, but with different length,
466    // 2 means only z/v differs
467    // -1 means having different field.
468    bestMatchDistanceInfo = 0;
469    int8_t fieldLength = sizeof(skeletonFieldWidth)/sizeof(skeletonFieldWidth[0]);
470
471    int32_t pos = -1;
472    const UHashElement* elem = NULL;
473    while ( (elem = fIntervalPatterns->nextElement(pos)) != NULL ) {
474        const UHashTok keyTok = elem->key;
475        UnicodeString* skeleton = (UnicodeString*)keyTok.pointer;
476#ifdef DTITVINF_DEBUG
477    skeleton->extract(0,  skeleton->length(), result, "UTF-8");
478    sprintf(mesg, "available skeletons: skeleton: %s; \n", result);
479    PRINTMESG(mesg)
480#endif
481
482        // clear skeleton field width
483        int8_t i;
484        for ( i = 0; i < fieldLength; ++i ) {
485            skeletonFieldWidth[i] = 0;
486        }
487        parseSkeleton(*skeleton, skeletonFieldWidth);
488        // calculate distance
489        int32_t distance = 0;
490        int8_t fieldDifference = 1;
491        for ( i = 0; i < fieldLength; ++i ) {
492            int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
493            int32_t fieldWidth = skeletonFieldWidth[i];
494            if ( inputFieldWidth == fieldWidth ) {
495                continue;
496            }
497            if ( inputFieldWidth == 0 ) {
498                fieldDifference = -1;
499                distance += DIFFERENT_FIELD;
500            } else if ( fieldWidth == 0 ) {
501                fieldDifference = -1;
502                distance += DIFFERENT_FIELD;
503            } else if (stringNumeric(inputFieldWidth, fieldWidth,
504                                     (char)(i+BASE) ) ) {
505                distance += STRING_NUMERIC_DIFFERENCE;
506            } else {
507                distance += (inputFieldWidth > fieldWidth) ?
508                            (inputFieldWidth - fieldWidth) :
509                            (fieldWidth - inputFieldWidth);
510            }
511        }
512        if ( distance < bestDistance ) {
513            bestSkeleton = skeleton;
514            bestDistance = distance;
515            bestMatchDistanceInfo = fieldDifference;
516        }
517        if ( distance == 0 ) {
518            bestMatchDistanceInfo = 0;
519            break;
520        }
521    }
522    if ( replaceZWithV && bestMatchDistanceInfo != -1 ) {
523        bestMatchDistanceInfo = 2;
524    }
525    return bestSkeleton;
526}
527
528
529
530DateIntervalInfo::IntervalPatternIndex
531DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
532                                               UErrorCode& status) {
533    if ( U_FAILURE(status) ) {
534        return kIPI_MAX_INDEX;
535    }
536    IntervalPatternIndex index = kIPI_MAX_INDEX;
537    switch ( field ) {
538      case UCAL_ERA:
539        index = kIPI_ERA;
540        break;
541      case UCAL_YEAR:
542        index = kIPI_YEAR;
543        break;
544      case UCAL_MONTH:
545        index = kIPI_MONTH;
546        break;
547      case UCAL_DATE:
548      case UCAL_DAY_OF_WEEK:
549      //case UCAL_DAY_OF_MONTH:
550        index = kIPI_DATE;
551        break;
552      case UCAL_AM_PM:
553        index = kIPI_AM_PM;
554        break;
555      case UCAL_HOUR:
556      case UCAL_HOUR_OF_DAY:
557        index = kIPI_HOUR;
558        break;
559      case UCAL_MINUTE:
560        index = kIPI_MINUTE;
561        break;
562      default:
563        status = U_ILLEGAL_ARGUMENT_ERROR;
564    }
565    return index;
566}
567
568
569
570void
571DateIntervalInfo::deleteHash(Hashtable* hTable)
572{
573    if ( hTable == NULL ) {
574        return;
575    }
576    int32_t pos = -1;
577    const UHashElement* element = NULL;
578    while ( (element = hTable->nextElement(pos)) != NULL ) {
579        const UHashTok keyTok = element->key;
580        const UHashTok valueTok = element->value;
581        const UnicodeString* value = (UnicodeString*)valueTok.pointer;
582        delete[] value;
583    }
584    delete fIntervalPatterns;
585}
586
587
588U_CDECL_BEGIN
589
590/**
591 * set hash table value comparator
592 *
593 * @param val1  one value in comparison
594 * @param val2  the other value in comparison
595 * @return      TRUE if 2 values are the same, FALSE otherwise
596 */
597static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
598
599static UBool
600U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
601    const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
602    const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
603    UBool ret = TRUE;
604    int8_t i;
605    for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret == TRUE; ++i ) {
606        ret = (pattern1[i] == pattern2[i]);
607    }
608    return ret;
609}
610
611U_CDECL_END
612
613
614Hashtable*
615DateIntervalInfo::initHash(UErrorCode& status) {
616    if ( U_FAILURE(status) ) {
617        return NULL;
618    }
619    Hashtable* hTable;
620    if ( (hTable = new Hashtable(FALSE, status)) == NULL ) {
621        status = U_MEMORY_ALLOCATION_ERROR;
622        return NULL;
623    }
624    hTable->setValueComparator(dtitvinfHashTableValueComparator);
625    return hTable;
626}
627
628
629void
630DateIntervalInfo::copyHash(const Hashtable* source,
631                           Hashtable* target,
632                           UErrorCode& status) {
633    if ( U_FAILURE(status) ) {
634        return;
635    }
636    int32_t pos = -1;
637    const UHashElement* element = NULL;
638    if ( source ) {
639        while ( (element = source->nextElement(pos)) != NULL ) {
640            const UHashTok keyTok = element->key;
641            const UnicodeString* key = (UnicodeString*)keyTok.pointer;
642            const UHashTok valueTok = element->value;
643            const UnicodeString* value = (UnicodeString*)valueTok.pointer;
644            UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
645            int8_t i;
646            for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
647                copy[i] = value[i];
648            }
649            target->put(UnicodeString(*key), copy, status);
650            if ( U_FAILURE(status) ) {
651                return;
652            }
653        }
654    }
655}
656
657
658U_NAMESPACE_END
659
660#endif
661