1/*
2**********************************************************************
3* Copyright (c) 2002-2012, International Business Machines
4* Corporation and others.  All Rights Reserved.
5**********************************************************************
6* Author: Alan Liu
7* Created: November 11 2002
8* Since: ICU 2.4
9**********************************************************************
10*/
11#include "utypeinfo.h"  // for 'typeid' to work
12
13#include "unicode/ustring.h"
14#include "unicode/strenum.h"
15#include "unicode/putil.h"
16#include "uenumimp.h"
17#include "ustrenum.h"
18#include "cstring.h"
19#include "cmemory.h"
20#include "uassert.h"
21
22U_NAMESPACE_BEGIN
23// StringEnumeration implementation ---------------------------------------- ***
24
25StringEnumeration::StringEnumeration()
26    : chars(charsBuffer), charsCapacity(sizeof(charsBuffer)) {
27}
28
29StringEnumeration::~StringEnumeration() {
30    if (chars != NULL && chars != charsBuffer) {
31        uprv_free(chars);
32    }
33}
34
35// StringEnumeration base class clone() default implementation, does not clone
36StringEnumeration *
37StringEnumeration::clone() const {
38  return NULL;
39}
40
41const char *
42StringEnumeration::next(int32_t *resultLength, UErrorCode &status) {
43    const UnicodeString *s=snext(status);
44    if(U_SUCCESS(status) && s!=NULL) {
45        unistr=*s;
46        ensureCharsCapacity(unistr.length()+1, status);
47        if(U_SUCCESS(status)) {
48            if(resultLength!=NULL) {
49                *resultLength=unistr.length();
50            }
51            unistr.extract(0, INT32_MAX, chars, charsCapacity, US_INV);
52            return chars;
53        }
54    }
55
56    return NULL;
57}
58
59const UChar *
60StringEnumeration::unext(int32_t *resultLength, UErrorCode &status) {
61    const UnicodeString *s=snext(status);
62    if(U_SUCCESS(status) && s!=NULL) {
63        unistr=*s;
64        if(resultLength!=NULL) {
65            *resultLength=unistr.length();
66        }
67        return unistr.getTerminatedBuffer();
68    }
69
70    return NULL;
71}
72
73const UnicodeString *
74StringEnumeration::snext(UErrorCode &status) {
75    int32_t length;
76    const char *s=next(&length, status);
77    return setChars(s, length, status);
78}
79
80void
81StringEnumeration::ensureCharsCapacity(int32_t capacity, UErrorCode &status) {
82    if(U_SUCCESS(status) && capacity>charsCapacity) {
83        if(capacity<(charsCapacity+charsCapacity/2)) {
84            // avoid allocation thrashing
85            capacity=charsCapacity+charsCapacity/2;
86        }
87        if(chars!=charsBuffer) {
88            uprv_free(chars);
89        }
90        chars=(char *)uprv_malloc(capacity);
91        if(chars==NULL) {
92            chars=charsBuffer;
93            charsCapacity=sizeof(charsBuffer);
94            status=U_MEMORY_ALLOCATION_ERROR;
95        } else {
96            charsCapacity=capacity;
97        }
98    }
99}
100
101UnicodeString *
102StringEnumeration::setChars(const char *s, int32_t length, UErrorCode &status) {
103    if(U_SUCCESS(status) && s!=NULL) {
104        if(length<0) {
105            length=(int32_t)uprv_strlen(s);
106        }
107
108        UChar *buffer=unistr.getBuffer(length+1);
109        if(buffer!=NULL) {
110            u_charsToUChars(s, buffer, length);
111            buffer[length]=0;
112            unistr.releaseBuffer(length);
113            return &unistr;
114        } else {
115            status=U_MEMORY_ALLOCATION_ERROR;
116        }
117    }
118
119    return NULL;
120}
121UBool
122StringEnumeration::operator==(const StringEnumeration& that)const {
123    return typeid(*this) == typeid(that);
124}
125
126UBool
127StringEnumeration::operator!=(const StringEnumeration& that)const {
128    return !operator==(that);
129}
130
131// UStringEnumeration implementation --------------------------------------- ***
132
133UStringEnumeration::UStringEnumeration(UEnumeration* _uenum) :
134    uenum(_uenum) {
135    U_ASSERT(_uenum != 0);
136}
137
138UStringEnumeration::~UStringEnumeration() {
139    uenum_close(uenum);
140}
141
142int32_t UStringEnumeration::count(UErrorCode& status) const {
143    return uenum_count(uenum, &status);
144}
145
146const char *UStringEnumeration::next(int32_t *resultLength, UErrorCode &status) {
147    return uenum_next(uenum, resultLength, &status);
148}
149
150const UnicodeString* UStringEnumeration::snext(UErrorCode& status) {
151    int32_t length;
152    const UChar* str = uenum_unext(uenum, &length, &status);
153    if (str == 0 || U_FAILURE(status)) {
154        return 0;
155    }
156    return &unistr.setTo(str, length);
157}
158
159void UStringEnumeration::reset(UErrorCode& status) {
160    uenum_reset(uenum, &status);
161}
162
163UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UStringEnumeration)
164U_NAMESPACE_END
165
166// C wrapper --------------------------------------------------------------- ***
167
168#define THIS(en) ((icu::StringEnumeration*)(en->context))
169
170U_CDECL_BEGIN
171
172/**
173 * Wrapper API to make StringEnumeration look like UEnumeration.
174 */
175static void U_CALLCONV
176ustrenum_close(UEnumeration* en) {
177    delete THIS(en);
178    uprv_free(en);
179}
180
181/**
182 * Wrapper API to make StringEnumeration look like UEnumeration.
183 */
184static int32_t U_CALLCONV
185ustrenum_count(UEnumeration* en,
186               UErrorCode* ec)
187{
188    return THIS(en)->count(*ec);
189}
190
191/**
192 * Wrapper API to make StringEnumeration look like UEnumeration.
193 */
194static const UChar* U_CALLCONV
195ustrenum_unext(UEnumeration* en,
196               int32_t* resultLength,
197               UErrorCode* ec)
198{
199    return THIS(en)->unext(resultLength, *ec);
200}
201
202/**
203 * Wrapper API to make StringEnumeration look like UEnumeration.
204 */
205static const char* U_CALLCONV
206ustrenum_next(UEnumeration* en,
207              int32_t* resultLength,
208              UErrorCode* ec)
209{
210    return THIS(en)->next(resultLength, *ec);
211}
212
213/**
214 * Wrapper API to make StringEnumeration look like UEnumeration.
215 */
216static void U_CALLCONV
217ustrenum_reset(UEnumeration* en,
218               UErrorCode* ec)
219{
220    THIS(en)->reset(*ec);
221}
222
223/**
224 * Pseudo-vtable for UEnumeration wrapper around StringEnumeration.
225 * The StringEnumeration pointer will be stored in 'context'.
226 */
227static const UEnumeration USTRENUM_VT = {
228    NULL,
229    NULL, // store StringEnumeration pointer here
230    ustrenum_close,
231    ustrenum_count,
232    ustrenum_unext,
233    ustrenum_next,
234    ustrenum_reset
235};
236
237U_CDECL_END
238
239/**
240 * Given a StringEnumeration, wrap it in a UEnumeration.  The
241 * StringEnumeration is adopted; after this call, the caller must not
242 * delete it (regardless of error status).
243 */
244U_CAPI UEnumeration* U_EXPORT2
245uenum_openFromStringEnumeration(icu::StringEnumeration* adopted, UErrorCode* ec) {
246    UEnumeration* result = NULL;
247    if (U_SUCCESS(*ec) && adopted != NULL) {
248        result = (UEnumeration*) uprv_malloc(sizeof(UEnumeration));
249        if (result == NULL) {
250            *ec = U_MEMORY_ALLOCATION_ERROR;
251        } else {
252            uprv_memcpy(result, &USTRENUM_VT, sizeof(USTRENUM_VT));
253            result->context = adopted;
254        }
255    }
256    if (result == NULL) {
257        delete adopted;
258    }
259    return result;
260}
261
262// C wrapper --------------------------------------------------------------- ***
263
264U_CDECL_BEGIN
265
266typedef struct UCharStringEnumeration {
267    UEnumeration uenum;
268    int32_t index, count;
269} UCharStringEnumeration;
270
271static void U_CALLCONV
272ucharstrenum_close(UEnumeration* en) {
273    uprv_free(en);
274}
275
276static int32_t U_CALLCONV
277ucharstrenum_count(UEnumeration* en,
278                   UErrorCode* /*ec*/) {
279    return ((UCharStringEnumeration*)en)->count;
280}
281
282static const UChar* U_CALLCONV
283ucharstrenum_unext(UEnumeration* en,
284                  int32_t* resultLength,
285                  UErrorCode* /*ec*/) {
286    UCharStringEnumeration *e = (UCharStringEnumeration*) en;
287    if (e->index >= e->count) {
288        return NULL;
289    }
290    const UChar* result = ((const UChar**)e->uenum.context)[e->index++];
291    if (resultLength) {
292        *resultLength = (int32_t)u_strlen(result);
293    }
294    return result;
295}
296
297
298static const char* U_CALLCONV
299ucharstrenum_next(UEnumeration* en,
300                  int32_t* resultLength,
301                  UErrorCode* /*ec*/) {
302    UCharStringEnumeration *e = (UCharStringEnumeration*) en;
303    if (e->index >= e->count) {
304        return NULL;
305    }
306    const char* result = ((const char**)e->uenum.context)[e->index++];
307    if (resultLength) {
308        *resultLength = (int32_t)uprv_strlen(result);
309    }
310    return result;
311}
312
313static void U_CALLCONV
314ucharstrenum_reset(UEnumeration* en,
315                   UErrorCode* /*ec*/) {
316    ((UCharStringEnumeration*)en)->index = 0;
317}
318
319static const UEnumeration UCHARSTRENUM_VT = {
320    NULL,
321    NULL, // store StringEnumeration pointer here
322    ucharstrenum_close,
323    ucharstrenum_count,
324    uenum_unextDefault,
325    ucharstrenum_next,
326    ucharstrenum_reset
327};
328
329static const UEnumeration UCHARSTRENUM_U_VT = {
330    NULL,
331    NULL, // store StringEnumeration pointer here
332    ucharstrenum_close,
333    ucharstrenum_count,
334    ucharstrenum_unext,
335    uenum_nextDefault,
336    ucharstrenum_reset
337};
338
339U_CDECL_END
340
341U_CAPI UEnumeration* U_EXPORT2
342uenum_openCharStringsEnumeration(const char* const strings[], int32_t count,
343                                 UErrorCode* ec) {
344    UCharStringEnumeration* result = NULL;
345    if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) {
346        result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration));
347        if (result == NULL) {
348            *ec = U_MEMORY_ALLOCATION_ERROR;
349        } else {
350            U_ASSERT((char*)result==(char*)(&result->uenum));
351            uprv_memcpy(result, &UCHARSTRENUM_VT, sizeof(UCHARSTRENUM_VT));
352            result->uenum.context = (void*)strings;
353            result->index = 0;
354            result->count = count;
355        }
356    }
357    return (UEnumeration*) result;
358}
359
360U_CAPI UEnumeration* U_EXPORT2
361uenum_openUCharStringsEnumeration(const UChar* const strings[], int32_t count,
362                                 UErrorCode* ec) {
363    UCharStringEnumeration* result = NULL;
364    if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) {
365        result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration));
366        if (result == NULL) {
367            *ec = U_MEMORY_ALLOCATION_ERROR;
368        } else {
369            U_ASSERT((char*)result==(char*)(&result->uenum));
370            uprv_memcpy(result, &UCHARSTRENUM_U_VT, sizeof(UCHARSTRENUM_U_VT));
371            result->uenum.context = (void*)strings;
372            result->index = 0;
373            result->count = count;
374        }
375    }
376    return (UEnumeration*) result;
377}
378
379
380// end C Wrapper
381