Tizen 2.1 base
[framework/osp/uifw.git] / src / graphics / text / FGrp_TextTextCutLinkParser.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Flora License, Version 1.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://floralicense.org/license/
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an AS IS BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /*
19  * @file        FGrp_TextTextCutLinkParser.cpp
20  * @brief       This is the implementation file for TextCutLinkParser class.
21  */
22
23 #include <new>
24 #include <FBaseCharacter.h>
25 #include <FBaseSysLog.h>
26 #include "FGrp_TextTextCutLinkParser.h"
27
28 using namespace Tizen::Base;
29 using namespace Tizen::Base::Collection;
30 using namespace Tizen::Base::Utility;
31 using namespace Tizen::Graphics;
32
33 static const int _MIN_TEXT_LENGTH = 5;
34 static const int _MIN_PHONE_NUMBER_LENGTH = 4;
35
36 static ArrayListT <String>* gpDomainNames = null;
37 static ArrayListT <String>* gpUrlPrefixes = null;
38
39 namespace Tizen { namespace Graphics
40 {
41
42 namespace _Text
43 {
44
45 static bool
46 IsGenericCharacter(wchar_t ch)
47 {
48         if ((ch >= L'a' && ch <= L'z') || (ch >= L'0' && ch <= L'9') ||
49                 (ch >= L'A' && ch <= L'Z') || ch == L'.' || ch == L'/' ||
50                 ch == L'=' || ch == L'&' || ch == L'%' || ch == L'-' ||
51                 ch == L'?' || ch == L':' || ch == L'@' || ch == L'_' ||
52                 ch == L'$' || ch == L';' || ch == L'+' || ch == L'!' ||
53                 ch == L'*' || ch == L'\'' || ch == L'(' || ch == L')' ||
54                 ch == L',')
55         {
56                 return true;
57         }
58         else
59         {
60                 return false;
61         }
62 }
63
64 static bool
65 IsEmailCharacter(wchar_t ch)
66 {
67         if ((ch >= L'a' && ch <= L'z') || (ch >= L'0' && ch <= L'9') ||
68                 (ch >= L'A' && ch <= L'Z') || ch == L'.' ||
69                 ch == L'-' || ch == L'_' || ch == L'+')
70         {
71                 return true;
72         }
73         else
74         {
75                 return false;
76         }
77 }
78
79 static bool
80 IsUrlCharacter(wchar_t ch)
81 {
82         if (ch == L'h' || ch == L'H' || ch == L'w' || ch == L'W')
83         {
84                 return true;
85         }
86         else
87         {
88                 return false;
89         }
90 }
91
92 static bool
93 IsPhoneNumCharacter(wchar_t ch)
94 {
95         if (Character::IsDigit(ch))
96         {
97                 return true;
98         }
99         else if (ch == L'*' || ch == L'#' || ch == L'+')
100         {
101                 return true;
102         }
103         else
104         {
105                 return false;
106         }
107 }
108
109 static bool
110 IsUrlAddress(const String& text, int index, int& linkLength)
111 {
112         ClearLastResult();
113         SysTryReturn(NID_GRP, index >= 0, false, E_INVALID_ARG,
114                                 "[E_INVALID_ARG] Index must be greater than or equal to 0.");
115
116         result r = E_SUCCESS;
117         bool dotFound = false;
118         bool validTextAfterDot = false;
119         int linkOffset = 0;
120         int totalLength = text.GetLength();
121         int length = 0;
122
123         String prefix(L"");
124         String subString(L"");
125         for (int i = 0; i < gpUrlPrefixes->GetCount(); i++)
126         {
127                 if (gpUrlPrefixes->GetAt(i, prefix) == E_SUCCESS)
128                 {
129                         length = prefix.GetLength();
130                         if (length <= 0)
131                         {
132                                 continue;
133                         }
134
135                         if (totalLength < index + length)
136                         {
137                                 break;
138                         }
139
140                         r = text.SubString(index, length, subString);
141                         SysTryReturn(
142                                 NID_GRP, r == E_SUCCESS, false, E_OUT_OF_RANGE,
143                                 "[E_OUT_OF_RANGE] SubString offset(%d) must greater than 0 and length(%d) must be less than total string length(%d).",
144                                 index, prefix.GetLength(), totalLength);
145                         if (prefix.CompareTo(subString) == 0)
146                         {
147                                 linkOffset = index;
148                                 while (index < totalLength && IsGenericCharacter(text[index]))
149                                 {
150                                         if (dotFound == false && text[index] == '.')
151                                         {
152                                                 dotFound = true;
153                                         }
154
155                                         if (validTextAfterDot == false && dotFound == true && text[index] != '.')
156                                         {
157                                                 validTextAfterDot = true;
158                                         }
159
160                                         index++;
161                                 }
162
163                                 if (dotFound == true && validTextAfterDot != true)
164                                 {
165                                         return false;
166                                 }
167
168                                 if (dotFound == true)
169                                 {
170                                         while (text[index - 1] == L'.' || text[index - 1] == L',')
171                                         {
172                                                 index--;
173                                         }
174                                 }
175                                 linkLength = index - linkOffset;
176
177                                 return true;
178                         }
179                 }
180         }
181
182         return false;
183 }
184
185 static bool
186 GetUrlLink(const String& text, int index, int lastLinkEndIndex, int domainLength, int& linkStartIndex, int& linkLength)
187 {
188         ClearLastResult();
189         SysTryReturn(NID_GRP, index >= 0, false, E_INVALID_ARG,
190                                 "[E_INVALID_ARG] Index must be greater than or equal to 0.");
191
192         bool dotFound = false;
193         bool validTextBeforeDot = false;
194         int linkOffset = index;
195
196         while (lastLinkEndIndex <= linkOffset)
197         {
198                 if (!IsGenericCharacter(text[linkOffset]) || text[linkOffset] == L'(' || text[linkOffset] == L'[')
199                 {
200                         linkOffset++;
201                         break;
202                 }
203
204                 if (dotFound == false && text[linkOffset] == L'.')
205                 {
206                         dotFound = true;
207                 }
208
209                 if (validTextBeforeDot == false && dotFound == true && text[linkOffset] != L'.')
210                 {
211                         validTextBeforeDot = true;
212                 }
213
214                 linkOffset--;
215         }
216
217         if (dotFound == true && validTextBeforeDot != true)
218         {
219                 return false;
220         }
221
222         if (dotFound == true)
223         {
224                 while (text[linkOffset] == L'.' || text[linkOffset] == L',')
225                 {
226                         linkOffset++;
227                 }
228         }
229
230         index += domainLength;
231         if (text[index] == L'/')
232         {
233                 while (index > 0)
234                 {
235                         if (!IsGenericCharacter(text[index]) && text[index] != L')' && text[index] != L']')
236                         {
237                                 break;
238                         }
239
240                         index++;
241                 }
242         }
243         else if (text[index] != null && text[index] != L' ' && text[index] != L')' &&
244                          text[index] != L']' && text[index] != 0xFFFC &&
245                          text[index] != 0x000A && text[index] != 0x000D)
246         {
247                 return false;
248         }
249
250         linkStartIndex = linkOffset;
251         linkLength = index - linkStartIndex;
252
253         return true;
254 }
255
256 static bool
257 IsDomainAddress(const String& text, int index, int lastLinkEndIndex, int& linkStartIndex, int& linkLength)
258 {
259         ClearLastResult();
260         result r = E_SUCCESS;
261
262         SysTryReturn(NID_GRP, index >= 0, false, E_INVALID_ARG,
263                                 "[E_INVALID_ARG] Index must be greater than or equal to 0.");
264
265         int domainLength = 0;
266         int length = 0;
267         int textLength = text.GetLength();
268
269         String subString(L"");
270         String domainAddress(L"");
271         for (int i = 0; i < gpDomainNames->GetCount(); i++)
272         {
273                 if (gpDomainNames->GetAt(i, domainAddress) == E_SUCCESS)
274                 {
275                         length = domainAddress.GetLength();
276                         if (length <= 0)
277                         {
278                                 continue;
279                         }
280
281                         if (textLength < index + length)
282                         {
283                                 break;
284                         }
285
286                         r = text.SubString(index, length, subString);
287                         SysTryReturn(NID_GRP, r == E_SUCCESS, false, E_OUT_OF_RANGE,
288                                 "[E_OUT_OF_RANGE] SubString offset(%d) must greater than 0 and length(%d) must be less than total string length(%d).",
289                                 index, domainAddress.GetLength(), text.GetLength());
290                         if (domainAddress.CompareTo(subString) == 0)
291                         {
292                                 domainLength = domainAddress.GetLength();
293                                 if (GetUrlLink(text, index, lastLinkEndIndex, domainLength, linkStartIndex, linkLength))
294                                 {
295                                         return true;
296                                 }
297                         }
298                 }
299         }
300
301         return false;
302 }
303
304 static bool
305 IsEmailAddress(const String& text, int index, int lastLinkEndIndex, int& linkStartIndex, int& linkLength)
306 {
307         ClearLastResult();
308         SysTryReturn(NID_GRP, index >= 0, false, E_INVALID_ARG,
309                                 "[E_INVALID_ARG] Index must be greater than or equal to 0.");
310
311         bool validTextAfterAt = false;
312         bool hasDot = false;
313         int atIndex = 0;
314         int totalLength = text.GetLength();
315         int linkOffset = index;
316
317         if (text[index + 1] == L'@')
318         {
319                 return false;
320         }
321
322         linkOffset--;
323
324         if (index > lastLinkEndIndex)
325         {
326                 while (IsEmailCharacter(text[linkOffset]) && lastLinkEndIndex <= linkOffset)
327                 {
328                         linkOffset--;
329                 }
330
331                 if (linkOffset == index - 1)
332                 {
333                         return false;
334                 }
335
336                 linkOffset++;
337
338                 atIndex = index;
339                 index++;
340                 while (IsEmailCharacter(text[index]) && index < totalLength)
341                 {
342                         if (validTextAfterAt == false)
343                         {
344                                 validTextAfterAt = true;
345                         }
346
347                         if (text[index] == '.')
348                         {
349                                 if (text[index + 1] == '.')
350                                 {
351                                         break;
352                                 }
353
354                                 if (hasDot == false)
355                                 {
356                                         hasDot = true;
357                                 }
358                         }
359
360                         index++;
361                 }
362
363                 if (validTextAfterAt == false || hasDot == false)
364                 {
365                         return false;
366                 }
367
368                 while (text[index - 1] == L'.')
369                 {
370                         index--;
371                 }
372
373                 linkStartIndex = linkOffset;
374                 linkLength = index - linkOffset;
375                 return true;
376         }
377
378         return false;
379 }
380
381 static bool
382 IsPhoneNumber(const String& text, int index, int& linkLength)
383 {
384         ClearLastResult();
385
386         int j = 1;
387         int k = 1;
388         bool isPhoneNumber = true;
389         int oneHyphen = false;
390         int oneDot = false;
391         int linkOffset = 0;
392         int blankCount = 0;
393
394         int totalLength = text.GetLength();
395         if (text[index] == L'+')
396         {
397                 k = 0;
398                 if (!Character::IsDigit(text[index + 1]))
399                 {
400                         return false;
401                 }
402         }
403
404         while (k < _MIN_PHONE_NUMBER_LENGTH)
405         {
406                 if (Character::IsDigit(text[index + j]) || text[index + j] == L'*' || text[index + j] == L'#')
407                 {
408                         k++;
409                         oneHyphen = false;
410                         oneDot = false;
411                 }
412                 else if (oneHyphen == false && text[index + j] == L'-')
413                 {
414                         oneHyphen = true;
415                 }
416                 else if (oneDot == false && text[index + j] == L'.')
417                 {
418                         oneDot = true;
419                 }
420                 else if (text[index + j] == L' ' || text[index + j] == L'(' || text[index + j] == L')')
421                 {
422                         // empty statement
423                 }
424                 else
425                 {
426                         isPhoneNumber = false;
427                         break;
428                 }
429
430                 j++;
431         }
432
433         if (isPhoneNumber)
434         {
435                 linkOffset = index;
436
437                 while (index < totalLength &&
438                            (Character::IsDigit(text[index]) || (text[index] == L'*') ||
439                                 (text[index] == L'#') || (text[index] == L'-') ||
440                                 (text[index] == L' ') || (text[index] == L'+') ||
441                                 (text[index] == L'.') || (text[index] == L'(') ||
442                                 (text[index] == L')') || (index == linkOffset && (text[linkOffset] == L'+'))))
443                 {
444                         index++;
445
446                         if (blankCount == 0 && text[index] == L' ')
447                         {
448                                 blankCount++;
449                         }
450                         else if (blankCount == 1 && text[index] == L' ')
451                         {
452                                 blankCount++;
453                                 break;
454                         }
455                         else if (Character::IsDigit(text[index]) || text[index] == L'*' || text[index] == L'#')
456                         {
457                                 blankCount = 0;
458                         }
459                         else if (text[index] == L'-' && text[index + 1] == L'-')
460                         {
461                                 break;
462                         }
463                         else if (text[index] == L'(' || text[index] == L')')
464                         {
465                                 break;
466                         }
467                         else if ((text[index] == L'.' &&
468                                           text[index + 1] == L'.') || (text[index] == L'.' && !Character::IsDigit(text[index + 1])))
469                         {
470                                 break;
471                         }
472                         else
473                         {
474                                 blankCount = 0;
475                         }
476                 }
477
478                 if (text[index - 1] == L' ')
479                 {
480                         index -= 1;
481                         while (text[index] == L' ')
482                         {
483                                 index--;
484                         }
485
486                         index++;
487                 }
488
489                 linkLength = index - linkOffset;
490                 return true;
491         }
492
493         return false;
494 }
495
496 TextCutLinkParser::TextCutLinkParser(void)
497 {
498         ClearLastResult();
499         result r = E_SUCCESS;
500
501         __linkMask = LINK_TYPE_URL | LINK_TYPE_EMAIL | LINK_TYPE_TEL_NUM;
502
503         if (!gpUrlPrefixes)
504         {
505                 gpUrlPrefixes = new (std::nothrow) ArrayListT <String>;
506                 SysTryCatch(NID_GRP
507                                 , gpUrlPrefixes != null
508                                 , r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient.");
509                 r = gpUrlPrefixes->Construct();
510                 SysTryCatch(NID_GRP
511                                 , r == E_SUCCESS
512                                 , , r, "[%s] Propagated.", GetErrorMessage(r));
513
514                 gpUrlPrefixes->Add(L"www.");
515                 gpUrlPrefixes->Add(L"http://");
516                 gpUrlPrefixes->Add(L"https://");
517                 gpUrlPrefixes->Add(L"wap.");
518                 gpUrlPrefixes->Add(L"wap2.");
519         }
520
521         if (!gpDomainNames)
522         {
523                 gpDomainNames = new (std::nothrow) ArrayListT <Tizen::Base::String>;
524                 SysTryCatch(NID_GRP
525                                 , gpDomainNames != null
526                                 , r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient.");
527
528                 r = gpDomainNames->Construct();
529                 SysTryCatch(NID_GRP
530                                 , r == E_SUCCESS
531                                 , , r, "[%s] Propagated.", GetErrorMessage(r));
532
533                 gpDomainNames->Add(L".com");
534                 gpDomainNames->Add(L".net");
535                 gpDomainNames->Add(L".kr");
536                 gpDomainNames->Add(L".be");
537                 gpDomainNames->Add(L".gr");
538                 gpDomainNames->Add(L".pt");
539                 gpDomainNames->Add(L".at");
540                 gpDomainNames->Add(L".gs");
541                 gpDomainNames->Add(L".ro");
542                 gpDomainNames->Add(L".edu");
543                 gpDomainNames->Add(L".as");
544                 gpDomainNames->Add(L".hk");
545                 gpDomainNames->Add(L".se");
546                 gpDomainNames->Add(L".org");
547                 gpDomainNames->Add(L".az");
548                 gpDomainNames->Add(L".il");
549                 gpDomainNames->Add(L".sg");
550                 gpDomainNames->Add(L".info");
551                 gpDomainNames->Add(L".bt");
552                 gpDomainNames->Add(L".in");
553                 gpDomainNames->Add(L".sh");
554                 gpDomainNames->Add(L".us");
555                 gpDomainNames->Add(L".cn");
556                 gpDomainNames->Add(L".io");
557                 gpDomainNames->Add(L".sk");
558                 gpDomainNames->Add(L".biz");
559                 gpDomainNames->Add(L".ac");
560                 gpDomainNames->Add(L".is");
561                 gpDomainNames->Add(L".so");
562                 gpDomainNames->Add(L".to");
563                 gpDomainNames->Add(L".af");
564                 gpDomainNames->Add(L".st");
565                 gpDomainNames->Add(L".ch");
566                 gpDomainNames->Add(L".al");
567                 gpDomainNames->Add(L".kz");
568                 gpDomainNames->Add(L".tc");
569                 gpDomainNames->Add(L".fr");
570                 gpDomainNames->Add(L".am");
571                 gpDomainNames->Add(L".li");
572                 gpDomainNames->Add(L".tf");
573                 gpDomainNames->Add(L".jp");
574                 gpDomainNames->Add(L".cx");
575                 gpDomainNames->Add(L".lu");
576                 gpDomainNames->Add(L".th");
577                 gpDomainNames->Add(L".de");
578                 gpDomainNames->Add(L".cz");
579                 gpDomainNames->Add(L".ly");
580                 gpDomainNames->Add(L".tj");
581                 gpDomainNames->Add(L".ru");
582                 gpDomainNames->Add(L".dz");
583                 gpDomainNames->Add(L".mc");
584                 gpDomainNames->Add(L".tm");
585                 gpDomainNames->Add(L".it");
586                 gpDomainNames->Add(L".ec");
587                 gpDomainNames->Add(L".mm");
588                 gpDomainNames->Add(L".asia");
589                 gpDomainNames->Add(L".mil");
590                 gpDomainNames->Add(L".ee");
591                 gpDomainNames->Add(L".ms");
592                 gpDomainNames->Add(L".me");
593                 gpDomainNames->Add(L".gov");
594                 gpDomainNames->Add(L".eg");
595                 gpDomainNames->Add(L".mx");
596                 gpDomainNames->Add(L".tel");
597                 gpDomainNames->Add(L".au");
598                 gpDomainNames->Add(L".es");
599                 gpDomainNames->Add(L".nl");
600                 gpDomainNames->Add(L".eu");
601                 gpDomainNames->Add(L".cc");
602                 gpDomainNames->Add(L".fo");
603                 gpDomainNames->Add(L".no");
604                 gpDomainNames->Add(L".tv");
605                 gpDomainNames->Add(L".ca");
606                 gpDomainNames->Add(L".ga");
607                 gpDomainNames->Add(L".nu");
608                 gpDomainNames->Add(L".name");
609                 gpDomainNames->Add(L".coop");
610                 gpDomainNames->Add(L".gf");
611                 gpDomainNames->Add(L".nz");
612                 gpDomainNames->Add(L".mobi");
613                 gpDomainNames->Add(L".dk");
614                 gpDomainNames->Add(L".gl");
615                 gpDomainNames->Add(L".pl");
616                 gpDomainNames->Add(L".tw");
617         }
618
619         return;
620 CATCH:
621         if (gpUrlPrefixes)
622         {
623                 delete gpUrlPrefixes;
624                 gpUrlPrefixes = null;
625         }
626         if (gpDomainNames)
627         {
628                 delete gpDomainNames;
629                 gpDomainNames = null;
630         }
631 }
632
633 TextCutLinkParser::~TextCutLinkParser(void)
634 {
635
636 }
637
638 TextLinkInfo*
639 TextCutLinkParser::Parse(const wchar_t* pText, int textLength, int startPosition)
640 {
641         ClearLastResult();
642         result r = E_SUCCESS;
643
644         if (__linkMask == LINK_TYPE_NONE)
645         {
646                 return null;
647         }
648
649         while (0 < startPosition)
650         {
651                 pText++;
652                 startPosition--;
653         }
654
655         String text(pText);
656         int linkStartIndex = 0;
657         int linkLength = 0;
658         int lastLinkEndIndex = 0;
659         bool linkParsed = false;
660         int totalLength = text.GetLength();
661         TextLinkInfo* pTextLink = null;
662         LinkType linkType = LINK_TYPE_NONE;
663
664         if (totalLength < _MIN_TEXT_LENGTH)
665         {
666                 return null;
667         }
668
669         pTextLink = CreateTextLinkInfo(0, 0, LINK_TYPE_NONE);
670         SysTryCatch(NID_GRP
671                         , pTextLink != null
672                         , , E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient.");
673
674         pTextLink->index = -1;
675
676         for (int i = 0; i < totalLength; )
677         {
678                 linkLength = 0;
679                 wchar_t checkLetter = 0;
680                 text.GetCharAt(i, checkLetter);
681
682                 if ((__linkMask & LINK_TYPE_URL)
683                                 && IsUrlCharacter(checkLetter) && IsUrlAddress(text, i, linkLength))
684                 {
685                         linkParsed = true;
686                         linkStartIndex = i;
687                         linkType = LINK_TYPE_URL;
688                 }
689                 else if ((__linkMask & LINK_TYPE_URL)
690                                 && (checkLetter == '.') && IsDomainAddress(text, i, lastLinkEndIndex, linkStartIndex, linkLength))
691                 {
692                         i = linkStartIndex;
693                         linkParsed = true;
694                         linkType = LINK_TYPE_URL;
695                 }
696                 else if ((__linkMask & LINK_TYPE_EMAIL)
697                                 && (checkLetter == '@') && IsEmailAddress(text, i, lastLinkEndIndex, linkStartIndex, linkLength))
698                 {
699                         linkParsed = true;
700                         i = linkStartIndex;
701                         linkType = LINK_TYPE_EMAIL;
702                 }
703                 else if ((__linkMask & LINK_TYPE_TEL_NUM)
704                                 && IsPhoneNumCharacter(checkLetter) && IsPhoneNumber(text, i, linkLength))
705                 {
706                         linkParsed = true;
707                         linkStartIndex = i;
708                         linkType = LINK_TYPE_TEL_NUM;
709                 }
710
711                 if (linkParsed)
712                 {
713                         if (pTextLink->index == -1)
714                         {
715                                 pTextLink->index = 0;
716                                 pTextLink->srcOffset = linkStartIndex;
717                                 pTextLink->length = linkLength;
718                                 pTextLink->linkType = linkType;
719                         }
720                         else
721                         {
722                                 TextLinkInfo* pNextLink = CreateTextLinkInfo(linkStartIndex, linkLength, linkType);
723                                 SysTryCatch(NID_GRP
724                                                 , pNextLink != null,
725                                                 r = E_OUT_OF_MEMORY, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient");
726                                 AppendTextLinkInfo(pTextLink, pNextLink);
727                         }
728
729                         i += linkLength;
730                         lastLinkEndIndex = i;
731                         linkParsed = false;
732                 }
733                 else
734                 {
735                         i++;
736                 }
737         }
738
739         if (pTextLink->index == -1)
740         {
741                 goto CATCH;
742         }
743         else
744         {
745                 return pTextLink;
746         }
747
748 CATCH:
749         if (pTextLink != null)
750         {
751                 delete pTextLink;
752                 pTextLink = null;
753         }
754
755         return null;
756 }
757
758 result
759 TextCutLinkParser::SetCutLinkMask(int mask)
760 {
761         SysTryReturn(NID_GRP
762                         , LINK_TYPE_NONE <= mask && mask < LINK_TYPE_MAX
763                         , E_INVALID_ARG, E_INVALID_ARG, "[E_INVALID_ARG] The argument is invalid.");
764
765         __linkMask = mask;
766
767         return E_SUCCESS;
768 }
769
770 int
771 TextCutLinkParser::GetCutLinkMask(void) const
772 {
773         return __linkMask;
774 }
775
776 bool
777 TextCutLinkParser::AppendTextLinkInfo(TextLinkInfo* pLinkInfo, TextLinkInfo* pNewLinkInfo)
778 {
779         TextLinkInfo* pTempLinkInfo = pLinkInfo;
780
781         while (1)
782         {
783                 if (pTempLinkInfo->pNextLinkInfo == null)
784                 {
785                         break;
786                 }
787                 pTempLinkInfo = pTempLinkInfo->pNextLinkInfo;
788         }
789
790         pTempLinkInfo->index = pTempLinkInfo->index + 1;
791         pTempLinkInfo->pNextLinkInfo = pNewLinkInfo;
792
793         return true;
794 }
795
796 TextLinkInfo*
797 TextCutLinkParser::CreateTextLinkInfo(int offset, int length, LinkType linkType)
798 {
799         TextLinkInfo* pTextLinkInfo = new (std::nothrow) TextLinkInfo;
800         SysTryReturn(NID_GRP
801                         , pTextLinkInfo != null
802                         , null, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient");
803
804         pTextLinkInfo->index = 0;
805         pTextLinkInfo->srcOffset = offset;
806         pTextLinkInfo->length = length;
807         pTextLinkInfo->linkType = linkType;
808
809         pTextLinkInfo->pNextLinkInfo = null;
810
811         return pTextLinkInfo;
812 }
813
814 }}} // Tizen::Graphics::_Text