1/*
2**********************************************************************
3* Copyright (c) 2002-2010, 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 "unicode/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(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(s!=NULL) {
63        unistr=*s;
64        if(U_SUCCESS(status)) {
65            if(resultLength!=NULL) {
66                *resultLength=unistr.length();
67            }
68            return unistr.getTerminatedBuffer();
69        }
70    }
71
72    return NULL;
73}
74
75void
76StringEnumeration::ensureCharsCapacity(int32_t capacity, UErrorCode &status) {
77    if(U_SUCCESS(status) && capacity>charsCapacity) {
78        if(capacity<(charsCapacity+charsCapacity/2)) {
79            // avoid allocation thrashing
80            capacity=charsCapacity+charsCapacity/2;
81        }
82        if(chars!=charsBuffer) {
83            uprv_free(chars);
84        }
85        chars=(char *)uprv_malloc(capacity);
86        if(chars==NULL) {
87            chars=charsBuffer;
88            charsCapacity=sizeof(charsBuffer);
89            status=U_MEMORY_ALLOCATION_ERROR;
90        } else {
91            charsCapacity=capacity;
92        }
93    }
94}
95
96UnicodeString *
97StringEnumeration::setChars(const char *s, int32_t length, UErrorCode &status) {
98    if(U_SUCCESS(status) && s!=NULL) {
99        if(length<0) {
100            length=(int32_t)uprv_strlen(s);
101        }
102
103        UChar *buffer=unistr.getBuffer(length+1);
104        if(buffer!=NULL) {
105            u_charsToUChars(s, buffer, length);
106            buffer[length]=0;
107            unistr.releaseBuffer(length);
108            return &unistr;
109        } else {
110            status=U_MEMORY_ALLOCATION_ERROR;
111        }
112    }
113
114    return NULL;
115}
116UBool
117StringEnumeration::operator==(const StringEnumeration& that)const {
118    return typeid(*this) == typeid(that);
119}
120
121UBool
122StringEnumeration::operator!=(const StringEnumeration& that)const {
123    return !operator==(that);
124}
125
126// UStringEnumeration implementation --------------------------------------- ***
127
128UStringEnumeration::UStringEnumeration(UEnumeration* _uenum) :
129    uenum(_uenum) {
130    U_ASSERT(_uenum != 0);
131}
132
133UStringEnumeration::~UStringEnumeration() {
134    uenum_close(uenum);
135}
136
137int32_t UStringEnumeration::count(UErrorCode& status) const {
138    return uenum_count(uenum, &status);
139}
140
141const UnicodeString* UStringEnumeration::snext(UErrorCode& status) {
142    int32_t length;
143    const UChar* str = uenum_unext(uenum, &length, &status);
144    if (str == 0 || U_FAILURE(status)) {
145        return 0;
146    }
147    return &unistr.setTo(str, length);
148}
149
150void UStringEnumeration::reset(UErrorCode& status) {
151    uenum_reset(uenum, &status);
152}
153
154UOBJECT_DEFINE_RTTI_IMPLEMENTATION(UStringEnumeration)
155U_NAMESPACE_END
156
157// C wrapper --------------------------------------------------------------- ***
158
159#define THIS(en) ((U_NAMESPACE_QUALIFIER StringEnumeration*)(en->context))
160
161U_CDECL_BEGIN
162
163/**
164 * Wrapper API to make StringEnumeration look like UEnumeration.
165 */
166static void U_CALLCONV
167ustrenum_close(UEnumeration* en) {
168    delete THIS(en);
169    uprv_free(en);
170}
171
172/**
173 * Wrapper API to make StringEnumeration look like UEnumeration.
174 */
175static int32_t U_CALLCONV
176ustrenum_count(UEnumeration* en,
177               UErrorCode* ec)
178{
179    return THIS(en)->count(*ec);
180}
181
182/**
183 * Wrapper API to make StringEnumeration look like UEnumeration.
184 */
185static const UChar* U_CALLCONV
186ustrenum_unext(UEnumeration* en,
187               int32_t* resultLength,
188               UErrorCode* ec)
189{
190    return THIS(en)->unext(resultLength, *ec);
191}
192
193/**
194 * Wrapper API to make StringEnumeration look like UEnumeration.
195 */
196static const char* U_CALLCONV
197ustrenum_next(UEnumeration* en,
198              int32_t* resultLength,
199              UErrorCode* ec)
200{
201    return THIS(en)->next(resultLength, *ec);
202}
203
204/**
205 * Wrapper API to make StringEnumeration look like UEnumeration.
206 */
207static void U_CALLCONV
208ustrenum_reset(UEnumeration* en,
209               UErrorCode* ec)
210{
211    THIS(en)->reset(*ec);
212}
213
214/**
215 * Pseudo-vtable for UEnumeration wrapper around StringEnumeration.
216 * The StringEnumeration pointer will be stored in 'context'.
217 */
218static const UEnumeration USTRENUM_VT = {
219    NULL,
220    NULL, // store StringEnumeration pointer here
221    ustrenum_close,
222    ustrenum_count,
223    ustrenum_unext,
224    ustrenum_next,
225    ustrenum_reset
226};
227
228U_CDECL_END
229
230/**
231 * Given a StringEnumeration, wrap it in a UEnumeration.  The
232 * StringEnumeration is adopted; after this call, the caller must not
233 * delete it (regardless of error status).
234 */
235U_CAPI UEnumeration* U_EXPORT2
236uenum_openFromStringEnumeration(U_NAMESPACE_QUALIFIER StringEnumeration* adopted, UErrorCode* ec) {
237    UEnumeration* result = NULL;
238    if (U_SUCCESS(*ec) && adopted != NULL) {
239        result = (UEnumeration*) uprv_malloc(sizeof(UEnumeration));
240        if (result == NULL) {
241            *ec = U_MEMORY_ALLOCATION_ERROR;
242        } else {
243            uprv_memcpy(result, &USTRENUM_VT, sizeof(USTRENUM_VT));
244            result->context = adopted;
245        }
246    }
247    if (result == NULL) {
248        delete adopted;
249    }
250    return result;
251}
252
253// C wrapper --------------------------------------------------------------- ***
254
255U_CDECL_BEGIN
256
257typedef struct UCharStringEnumeration {
258    UEnumeration uenum;
259    int32_t index, count;
260} UCharStringEnumeration;
261
262static void U_CALLCONV
263ucharstrenum_close(UEnumeration* en) {
264    uprv_free(en);
265}
266
267static int32_t U_CALLCONV
268ucharstrenum_count(UEnumeration* en,
269                   UErrorCode* /*ec*/) {
270    return ((UCharStringEnumeration*)en)->count;
271}
272
273static const char* U_CALLCONV
274ucharstrenum_next(UEnumeration* en,
275                  int32_t* resultLength,
276                  UErrorCode* /*ec*/) {
277    UCharStringEnumeration *e = (UCharStringEnumeration*) en;
278    if (e->index >= e->count) {
279        return NULL;
280    }
281    const char* result = ((const char**)e->uenum.context)[e->index++];
282    if (resultLength) {
283        *resultLength = (int32_t)uprv_strlen(result);
284    }
285    return result;
286}
287
288static void U_CALLCONV
289ucharstrenum_reset(UEnumeration* en,
290                   UErrorCode* /*ec*/) {
291    ((UCharStringEnumeration*)en)->index = 0;
292}
293
294static const UEnumeration UCHARSTRENUM_VT = {
295    NULL,
296    NULL, // store StringEnumeration pointer here
297    ucharstrenum_close,
298    ucharstrenum_count,
299    uenum_unextDefault,
300    ucharstrenum_next,
301    ucharstrenum_reset
302};
303
304U_CDECL_END
305
306U_CAPI UEnumeration* U_EXPORT2
307uenum_openCharStringsEnumeration(const char* const* strings, int32_t count,
308                                 UErrorCode* ec) {
309    UCharStringEnumeration* result = NULL;
310    if (U_SUCCESS(*ec) && count >= 0 && (count == 0 || strings != 0)) {
311        result = (UCharStringEnumeration*) uprv_malloc(sizeof(UCharStringEnumeration));
312        if (result == NULL) {
313            *ec = U_MEMORY_ALLOCATION_ERROR;
314        } else {
315            U_ASSERT((char*)result==(char*)(&result->uenum));
316            uprv_memcpy(result, &UCHARSTRENUM_VT, sizeof(UCHARSTRENUM_VT));
317            result->uenum.context = (void*)strings;
318            result->index = 0;
319            result->count = count;
320        }
321    }
322    return (UEnumeration*) result;
323}
324
325
326