|
1 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // CFreagmentedString.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 // header |
|
19 #include <cfragmentedstring.h> |
|
20 |
|
21 // constants |
|
22 const TInt KFragmentedStringGranularity=128; |
|
23 |
|
24 /** Constructor */ |
|
25 EXPORT_C CFragmentedString::CFragmentedString() |
|
26 : CArrayPtrFlat<HBufC>(KFragmentedStringGranularity) |
|
27 { |
|
28 } |
|
29 |
|
30 /** Destructor. |
|
31 |
|
32 This resets and destroys the HBufC array. |
|
33 */ |
|
34 EXPORT_C CFragmentedString::~CFragmentedString() |
|
35 { |
|
36 ResetAndDestroy(); |
|
37 } |
|
38 |
|
39 /** Copies a sub-string and then appends the copy to the string. |
|
40 |
|
41 @param aString Sub-string to append |
|
42 */ |
|
43 EXPORT_C void CFragmentedString::AddStringL(const TDesC& aString) |
|
44 // Adds the string to our internal array of strings |
|
45 { |
|
46 HBufC* string=aString.AllocL(); |
|
47 AddStringL(string); |
|
48 } |
|
49 |
|
50 /** Appends a sub-string to the string. |
|
51 |
|
52 @param aString Sub-string to append |
|
53 */ |
|
54 EXPORT_C void CFragmentedString::AddStringL(HBufC* aString) |
|
55 // Adds the string to our internal array of strings |
|
56 { |
|
57 // take ownership of aString |
|
58 CleanupStack::PushL(aString); |
|
59 AppendL(aString); |
|
60 CleanupStack::Pop(); // aString |
|
61 } |
|
62 |
|
63 /** Gets the length of the string. |
|
64 |
|
65 @return String length |
|
66 */ |
|
67 EXPORT_C TInt CFragmentedString::Length() const |
|
68 // Calculates the entire length of our 'complete' string. |
|
69 { |
|
70 if (Count()==0) |
|
71 CONST_CAST(CFragmentedString*, this)->iCurrentCharacter=0; |
|
72 |
|
73 TInt len = -iCurrentCharacter; |
|
74 for (TInt index = iCurrentIndex; index < Count(); index++) |
|
75 len+=At(index)->Length(); |
|
76 return len; |
|
77 } |
|
78 |
|
79 /** Gets a string containing all sub-strings after the current position in a newly-allocated buffer. |
|
80 |
|
81 It is the caller's responsibility to clean up the returned string. |
|
82 |
|
83 @return String containing all sub-strings after the current position in a newly-allocated buffer |
|
84 */ |
|
85 EXPORT_C HBufC* CFragmentedString::StringL() const |
|
86 // Returns a newly allocated non-fragmented version of our string. |
|
87 { |
|
88 if (Count()>0) |
|
89 return StringL(iCurrentIndex, iCurrentCharacter, Count(), At(Count()-1)->Length()); |
|
90 else |
|
91 return HBufC::NewL(1); |
|
92 } |
|
93 |
|
94 /** Gets the entire string in a newly-allocated buffer. |
|
95 |
|
96 It is the caller's responsibility to clean up the returned string. |
|
97 |
|
98 @return Entire string in a newly-allocated buffer |
|
99 */ |
|
100 EXPORT_C HBufC* CFragmentedString::ContentL() const |
|
101 // Returns a newly allocated non-fragmented version of our string. |
|
102 { |
|
103 if (Count()>0) |
|
104 return StringL(0, 0, Count(), At(Count()-1)->Length()); |
|
105 else |
|
106 return HBufC::NewL(1); |
|
107 } |
|
108 |
|
109 void CFragmentedString::StartMatch() |
|
110 { |
|
111 iMatched = ENoMatch; |
|
112 iMatchedToIndex = iCurrentIndex; |
|
113 iMatchedToCharacter = iCurrentCharacter; |
|
114 } |
|
115 |
|
116 |
|
117 // could be made more efficient by matching blocks of text |
|
118 /** Tests if a specified target string occurs at the current position. |
|
119 |
|
120 The function does a byte-for-byte comparison of the string contents from the current position against aString. |
|
121 |
|
122 @return Match result |
|
123 @param aString String to attempt to match |
|
124 */ |
|
125 EXPORT_C CFragmentedString::TStringMatch CFragmentedString::Match(const TDesC& aString) |
|
126 { |
|
127 TInt stringIndex = 0; |
|
128 StartMatch(); |
|
129 const TText* stringPtr=aString.Ptr(); |
|
130 |
|
131 while (iMatchedToIndex<Count() && stringIndex<aString.Length()) |
|
132 { |
|
133 HBufC const* current=At(iMatchedToIndex); |
|
134 const TText* currentPtr=current->Ptr()+iMatchedToCharacter; |
|
135 while (stringIndex<aString.Length() && iMatchedToCharacter<current->Length()) |
|
136 { |
|
137 if (*stringPtr!=*currentPtr) |
|
138 return ENoMatch; |
|
139 ++iMatchedToCharacter; |
|
140 ++stringIndex; |
|
141 ++stringPtr; |
|
142 ++currentPtr; |
|
143 } |
|
144 |
|
145 if (iMatchedToCharacter==current->Length()) |
|
146 { |
|
147 iMatchedToCharacter=0; |
|
148 iMatchedToIndex++; |
|
149 } |
|
150 } |
|
151 |
|
152 if (stringIndex<aString.Length()) |
|
153 iMatched=EInsufficientData; |
|
154 else |
|
155 iMatched=EMatch; |
|
156 |
|
157 return iMatched; |
|
158 } |
|
159 |
|
160 |
|
161 TBool CFragmentedString::FindNextMatchChar(TUint& aChar) |
|
162 { |
|
163 const TInt numberOfFragments=Count(); |
|
164 |
|
165 TBool finished=EFalse; |
|
166 while (iMatchedToIndex<numberOfFragments && !finished) |
|
167 { |
|
168 HBufC* current = At(iMatchedToIndex); |
|
169 finished=iMatchedToCharacter<current->Length(); |
|
170 if (!finished) |
|
171 { |
|
172 iMatchedToCharacter = 0; |
|
173 iMatchedToIndex++; |
|
174 } |
|
175 } |
|
176 |
|
177 /*if (iMatchedToIndex<numberOfFragments && iMatchedToCharacter>=At(iMatchedToIndex)->Length()) |
|
178 { |
|
179 iMatchedToCharacter=0; |
|
180 while (iMatchedToIndex<numberOfFragments && At(iMatchedToIndex)->Length()==0) |
|
181 iMatchedToIndex++; |
|
182 } |
|
183 */ |
|
184 |
|
185 const TBool foundChar=iMatchedToIndex<numberOfFragments && iMatchedToCharacter<At(iMatchedToIndex)->Length(); |
|
186 |
|
187 if (foundChar) |
|
188 { |
|
189 HBufC const* current = At(iMatchedToIndex); |
|
190 aChar=(*current)[iMatchedToCharacter]; |
|
191 } |
|
192 |
|
193 return foundChar; |
|
194 } |
|
195 |
|
196 |
|
197 /** Tests if the character at the current position is within a specified character code range. |
|
198 |
|
199 @return Match result |
|
200 @param aLower Lower range (inclusive) for a match |
|
201 @param aUpper Upper range (inclusive) for a match |
|
202 */ |
|
203 EXPORT_C CFragmentedString::TStringMatch CFragmentedString::MatchRange(TUint aLower, TUint aUpper) |
|
204 { |
|
205 StartMatch(); |
|
206 TUint character; |
|
207 if (FindNextMatchChar(character)) |
|
208 { |
|
209 if (character>=aLower && character<=aUpper) |
|
210 { |
|
211 iMatched=EMatch; |
|
212 ++iMatchedToCharacter; |
|
213 } |
|
214 } |
|
215 else |
|
216 iMatched = EInsufficientData; |
|
217 |
|
218 return iMatched; |
|
219 } |
|
220 |
|
221 CFragmentedString::TStringMatch CFragmentedString::DoMatchSelect(const TDesC& aSelection, TBool aInSelection) |
|
222 { |
|
223 StartMatch(); |
|
224 TUint character; |
|
225 if (FindNextMatchChar(character)) |
|
226 { |
|
227 if ((aSelection.Locate(character)==KErrNotFound)!=aInSelection) |
|
228 { |
|
229 iMatched=EMatch; |
|
230 ++iMatchedToCharacter; |
|
231 } |
|
232 } |
|
233 else |
|
234 iMatched = EInsufficientData; |
|
235 |
|
236 return iMatched; |
|
237 } |
|
238 |
|
239 /** Tests if the character at the current position matches any character in a specified string. |
|
240 |
|
241 @return Match result |
|
242 @param aSelection String specifying one or more characters, any of which will result in a successful match |
|
243 */ |
|
244 EXPORT_C CFragmentedString::TStringMatch CFragmentedString::MatchSelect(const TDesC& aSelection) |
|
245 { |
|
246 return DoMatchSelect(aSelection, ETrue); |
|
247 } |
|
248 |
|
249 /** Tests if the character at the current position does not match any character in a specified string. |
|
250 |
|
251 @return Match result |
|
252 @param aSelection String specifying one or more characters, any of which will result in a failed match |
|
253 */ |
|
254 EXPORT_C CFragmentedString::TStringMatch CFragmentedString::MatchNotSelect(const TDesC& aSelection) |
|
255 { |
|
256 return DoMatchSelect(aSelection, EFalse); |
|
257 } |
|
258 |
|
259 /** Removes and deletes all sub-strings occurring before the position of the last successful match. |
|
260 |
|
261 Any sub-strings at or after a mark are not deleted, regardless of the last successful match position. */ |
|
262 EXPORT_C void CFragmentedString::ConsumeMatched() |
|
263 { |
|
264 // update currents |
|
265 iCurrentIndex = iMatchedToIndex; |
|
266 iCurrentCharacter = iMatchedToCharacter; |
|
267 |
|
268 // find out if we can delete any parts |
|
269 TInt freeIndex; |
|
270 if (iMarkStack.IsEmpty()) |
|
271 freeIndex=0; |
|
272 else |
|
273 { |
|
274 TStringMark* stringMark=iMarkStack.Last(); |
|
275 freeIndex=stringMark->iMarkIndex-1; |
|
276 } |
|
277 |
|
278 // delete any parts which are no longer needed |
|
279 if (freeIndex<iCurrentIndex && freeIndex>=0) |
|
280 { |
|
281 for (TInt index=iMarkStack.Count(); --index>=0;) |
|
282 iMarkStack[index]->iMarkIndex-=freeIndex+1; |
|
283 |
|
284 for (TInt stringIndex=freeIndex; stringIndex>=0; stringIndex--) |
|
285 delete(At(stringIndex)); |
|
286 Delete(0,freeIndex+1); |
|
287 iCurrentIndex-=freeIndex+1; |
|
288 iMatchedToIndex-=freeIndex+1; |
|
289 } |
|
290 } |
|
291 |
|
292 /** Resets the string. |
|
293 |
|
294 This resets and destroys the HBufC array, and clears all indexes and marks. */ |
|
295 EXPORT_C void CFragmentedString::Reset() |
|
296 { |
|
297 ResetAndDestroy(); |
|
298 iMarkStack.Clear(); |
|
299 iCurrentIndex = 0; |
|
300 iCurrentCharacter = 0; |
|
301 } |
|
302 |
|
303 HBufC* CFragmentedString::StringL(TInt aStartIndex, TInt aStartCharacter, TInt aEndIndex, TInt aEndCharacter, const TDesC* aInitialText/*=NULL*/) const |
|
304 { |
|
305 // Work out how long our string is going to be |
|
306 TInt markedLength=-aStartCharacter; |
|
307 TInt index; |
|
308 for (index=aStartIndex; index<aEndIndex; index++) |
|
309 markedLength+=At(index)->Length(); |
|
310 if (aEndIndex<Count()) |
|
311 markedLength+=aEndCharacter; |
|
312 |
|
313 const TInt extraLen=aInitialText?aInitialText->Length():0; |
|
314 HBufC* markedString = NULL; |
|
315 if(markedLength || extraLen) |
|
316 { |
|
317 // Now allocate the string |
|
318 markedString = HBufC::NewL(markedLength+extraLen); |
|
319 TPtr markedStringPtr = markedString->Des(); |
|
320 |
|
321 if (aInitialText) |
|
322 markedStringPtr.Append(*aInitialText); |
|
323 // Now build the string up |
|
324 if (aStartIndex == aEndIndex) |
|
325 markedStringPtr.Append(At(aStartIndex)->Mid(aStartCharacter, markedLength)); |
|
326 else |
|
327 { |
|
328 if (aStartIndex < aEndIndex) |
|
329 markedStringPtr.Append(At(aStartIndex)->Mid(aStartCharacter)); |
|
330 for (index = aStartIndex+1; index < aEndIndex; index++) |
|
331 markedStringPtr.Append(*At(index)); |
|
332 if (aEndIndex < Count()) |
|
333 markedStringPtr.Append(At(aEndIndex)->Left(aEndCharacter)); |
|
334 } |
|
335 |
|
336 } |
|
337 return markedString; |
|
338 } |
|
339 |
|
340 /** Gets a new string containing the string contents from the head mark to the current index position, prepended with a specified string. |
|
341 |
|
342 It is the caller's responsibility to clean up the returned string. |
|
343 |
|
344 @return New string containing aInitialText and then the marked contents |
|
345 @param aInitialText String to prepend to the result |
|
346 */ |
|
347 EXPORT_C HBufC* CFragmentedString::MarkedWithInitialTextL(const TDesC& aInitialText) |
|
348 { |
|
349 TStringMark* stringMark = iMarkStack.Head(); |
|
350 return StringL(stringMark->iMarkIndex, stringMark->iMarkCharacter, iCurrentIndex, iCurrentCharacter, &aInitialText); |
|
351 } |
|
352 |
|
353 /** Gets a new string containing the string contents from the head mark to the current index position. |
|
354 |
|
355 It is the caller's responsibility to clean up the returned string. |
|
356 |
|
357 @return New string containing marked contents |
|
358 */ |
|
359 EXPORT_C HBufC* CFragmentedString::MarkedL() |
|
360 { |
|
361 TStringMark* stringMark = iMarkStack.Head(); |
|
362 return StringL(stringMark->iMarkIndex, stringMark->iMarkCharacter, iCurrentIndex, iCurrentCharacter); |
|
363 } |
|
364 |
|
365 /** Adds a mark at the current index position. |
|
366 |
|
367 This mark becomes the head mark. |
|
368 |
|
369 Note this function can leave with an out of memory error. |
|
370 */ |
|
371 EXPORT_C void CFragmentedString::Mark() // Mark can leave |
|
372 { |
|
373 TStringMark* stringMark = new (ELeave) TStringMark(iCurrentIndex, iCurrentCharacter); |
|
374 iMarkStack.PushL(stringMark); |
|
375 } |
|
376 |
|
377 /** Deletes the head mark. */ |
|
378 EXPORT_C void CFragmentedString::DeleteMark() |
|
379 { |
|
380 TStringMark* stringMark = iMarkStack.Pop(); |
|
381 delete stringMark; |
|
382 } |
|
383 |
|
384 // move current position to top (head) mark |
|
385 /** Moves the current index position to the head mark. */ |
|
386 EXPORT_C void CFragmentedString::ResetToMark() |
|
387 { |
|
388 TStringMark* stringMark = iMarkStack.Head(); |
|
389 iCurrentIndex = stringMark->iMarkIndex; |
|
390 iCurrentCharacter = stringMark->iMarkCharacter; |
|
391 } |
|
392 |
|
393 /** Replaces the string contents to the head mark with a specified string. |
|
394 |
|
395 @param aString Replacment string |
|
396 */ |
|
397 EXPORT_C void CFragmentedString::ReplaceMarkedL(HBufC* aString) |
|
398 { |
|
399 TStringMark* stringMark = iMarkStack.Head(); |
|
400 DeleteToMark(*stringMark); |
|
401 InsertStringL(aString); |
|
402 } |
|
403 |
|
404 /** Replaces the string contents to the head mark with a specified string, and then |
|
405 advances the current index position to the next sub-string. |
|
406 |
|
407 @param aString Replacment string |
|
408 */ |
|
409 EXPORT_C void CFragmentedString::ReplaceMarkedAndSkipL(HBufC* aString) |
|
410 { |
|
411 ReplaceMarkedL(aString); |
|
412 iCurrentIndex++; |
|
413 } |
|
414 |
|
415 // could be speeded up |
|
416 /** Deletes from the current index position to the specified mark. |
|
417 |
|
418 @param aStringMark Mark to delete to |
|
419 */ |
|
420 EXPORT_C void CFragmentedString::DeleteToMark(const TStringMark& aStringMark) |
|
421 { |
|
422 if (iCurrentIndex>=Count()) |
|
423 { |
|
424 iCurrentIndex=Count()-1; |
|
425 if (iCurrentIndex==-1) |
|
426 { |
|
427 iCurrentIndex=0; |
|
428 iCurrentCharacter=0; |
|
429 } |
|
430 else |
|
431 iCurrentCharacter=At(iCurrentIndex)->Length(); |
|
432 } |
|
433 |
|
434 while (iCurrentIndex > aStringMark.iMarkIndex |
|
435 || (iCurrentIndex==aStringMark.iMarkIndex |
|
436 && iCurrentCharacter>aStringMark.iMarkCharacter)) |
|
437 { |
|
438 if(--iCurrentCharacter<0) |
|
439 { |
|
440 if(At(iCurrentIndex)->Length()==0) |
|
441 Delete(iCurrentIndex); |
|
442 |
|
443 iCurrentIndex--; |
|
444 iCurrentCharacter = At(iCurrentIndex)->Length(); |
|
445 } |
|
446 At(iCurrentIndex)->Des().Delete(iCurrentCharacter,1); |
|
447 } |
|
448 } |
|
449 |
|
450 /** Inserts a specified string at the current index position. |
|
451 |
|
452 @param aString String to insert |
|
453 */ |
|
454 EXPORT_C void CFragmentedString::InsertStringL(HBufC* aString) |
|
455 { |
|
456 InsertStringToL(aString, iCurrentIndex, iCurrentCharacter); |
|
457 } |
|
458 |
|
459 /** Inserts a string at a specified position. |
|
460 |
|
461 1. if aLengthIntoString is 0, the function inserts a new sub-string at array position aStringIndex |
|
462 |
|
463 2. if aLengthIntoString is equal to the length of the sub-string at aStringIndex, then it inserts a new sub-string at array position aStringIndex+1. |
|
464 |
|
465 3. If aLengthIntoString is in the middle of theaStringIndex sub-string, then it: |
|
466 |
|
467 a) inserts a new sub-string at aStringIndex+1 holdingaString |
|
468 |
|
469 b) inserts a new sub-string at aStringIndex+2 holding the data from aStringIndex after aLengthIntoString |
|
470 |
|
471 c) truncates the original aStringIndex to hold only the data before aLengthIntoString |
|
472 |
|
473 @param aString String to insert |
|
474 @param aStringIndex Array index of the sub-string at which to insert |
|
475 @param aLengthIntoString Character position within the sub-string at which to insert |
|
476 */ |
|
477 EXPORT_C void CFragmentedString::InsertStringToL(HBufC* aString, TInt aStringIndex, TInt aLengthIntoString) |
|
478 { |
|
479 if (aString==NULL || aString->Length()==0) |
|
480 return; |
|
481 |
|
482 // In the beginning of a string |
|
483 if (aLengthIntoString==0) |
|
484 { |
|
485 InsertL(aStringIndex, aString); |
|
486 return; |
|
487 } |
|
488 |
|
489 // At the end of a string |
|
490 if (aLengthIntoString==At(aStringIndex)->Length()) |
|
491 { |
|
492 InsertL(aStringIndex+1, aString); |
|
493 return; |
|
494 } |
|
495 |
|
496 // In the middle |
|
497 TPtrC remainder = At(iCurrentIndex)->Mid(iCurrentCharacter); |
|
498 InsertL(aStringIndex+1, aString); |
|
499 InsertL(aStringIndex+2, remainder.AllocL() ); |
|
500 At(iCurrentIndex)->Des().SetLength(iCurrentCharacter); |
|
501 iCurrentIndex++; |
|
502 iCurrentCharacter = 0; |
|
503 } |