sync with tizen_2.0
[platform/framework/native/appfw.git] / src / app / FApp_AppResourceString.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
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        FApp_AppResourceString.cpp
20  * @brief       This is the implementation for the _AppResourceString class.
21  */
22
23 #include <libxml/parser.h>
24 #include <libxml/xpath.h>
25 #include <libxml/tree.h>
26 #include <unique_ptr.h>
27 #include <runtime_info.h>
28 #include <unicode/locid.h>
29
30 #include <FBaseObject.h>
31 #include <FBaseString.h>
32 #include <FBaseResult.h>
33 #include <FIoFile.h>
34 #include <FIoDirectory.h>
35 #include <FSysSystemInfo.h>
36 #include <FSys_SettingInfoImpl.h>
37
38 #include <FBaseSysLog.h>
39 #include <FAppPkgPackageManager.h>
40 #include <FAppPkgPackageInfo.h>
41 #include <FBase_StringConverter.h>
42 #include <FApp_AppInfo.h>
43
44 #include "FApp_AppResourceString.h"
45 #include "FAppPkg_PackageManagerImpl.h"
46 #include "FAppPkg_PackageInfoImpl.h"
47
48 using namespace Tizen::App::Package;
49 using namespace Tizen::Base;
50 using namespace Tizen::Io;
51 using namespace Tizen::Locales;
52 using namespace Tizen::System;
53
54 static const char* AR_ROOT_NODE_NAME = "string_table";
55 static const char* AR_CHILD_NODE_1 = "text";
56 static const char* AR_ATTRIBUTE = "id";
57
58 static xmlNodePtr gCur = 0;
59
60 namespace Tizen { namespace App
61 {
62
63 _AppResourceString::_AppResourceString(void)
64         : __pDoc(null)
65         , __pLockOfParser(null)
66 {
67 }
68
69
70 result
71 _AppResourceString::Construct(const Tizen::Locales::Locale& locale)
72 {
73         return Construct();
74 }
75
76
77 result
78 _AppResourceString::Construct(void)
79 {
80         return Initialize();
81 }
82
83 result
84 _AppResourceString::Construct(const AppId& appId)
85 {
86         return Initialize(appId);
87 }
88
89 result
90 _AppResourceString::Initialize(void)
91 {
92         result r = E_SUCCESS;
93         if (__pLockOfParser == null)
94         {
95                 __pLockOfParser = new (std::nothrow) Tizen::Base::Runtime::Mutex();
96                 SysTryReturnResult(NID_APP, __pLockOfParser != null, E_OUT_OF_MEMORY, "Failed to initialize resource parser.");
97
98                 r = __pLockOfParser->Create();
99                 SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to Create Mutex.", GetErrorMessage(r));
100         }
101
102
103         r = __pLockOfParser->Acquire();
104         SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to Acquire Mutex.", GetErrorMessage(r));
105
106         r = InitializeStringInfo();
107         SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to InitializeStringInfo.", GetErrorMessage(r));
108
109         __pLockOfParser->Release();
110         return E_SUCCESS;
111
112 CATCH:
113         delete __pLockOfParser;
114         __pLockOfParser = null;
115
116         return r;
117 }
118
119 result
120 _AppResourceString::Initialize(const AppId& appId)
121 {
122         result r = E_SUCCESS;
123         if (__pLockOfParser == null)
124         {
125                 __pLockOfParser = new (std::nothrow) Tizen::Base::Runtime::Mutex();
126                 SysTryReturnResult(NID_APP, __pLockOfParser != null, E_OUT_OF_MEMORY, "Failed to initialize resource parser.");
127
128                 r = __pLockOfParser->Create();
129                 SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to Create Mutex.", GetErrorMessage(r));
130         }
131
132         r = __pLockOfParser->Acquire();
133         SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to Acquire Mutex.", GetErrorMessage(r));
134
135         r = InitializeStringInfo(appId);
136         SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to InitializeStringInfo.", GetErrorMessage(r));
137
138         __pLockOfParser->Release();
139         return E_SUCCESS;
140
141 CATCH:
142         delete __pLockOfParser;
143         __pLockOfParser = null;
144
145         return r;
146 }
147
148
149
150 result
151 _AppResourceString::InitializeStringInfo(void)
152 {
153         if (__pDoc != null)
154         {
155                 SysLog(NID_APP, "__pDoc is freed to reinitialize.");
156                 xmlFreeDoc(__pDoc);
157                 __pDoc = null;
158         }
159
160         char* pValue = null;
161         int ret = runtime_info_get_value_string(RUNTIME_INFO_KEY_LANGUAGE, &pValue);
162         SysTryReturnResult(NID_APP, ret == 0, E_FAILURE, "[E_SYSTEM] runtime_info_get_value_string returns %d.", ret);
163
164         U_ICU_NAMESPACE::Locale icuLcl(pValue);
165
166         String language(icuLcl.getISO3Language());
167         String country(icuLcl.getCountry());
168
169 //      SysLog(NID_APP, "res/%ls-%ls.xml", language.GetPointer(), country.GetPointer());
170
171         String resFilename;
172         result r = resFilename.Format(256 * 2 + 10, L"res/%ls-%ls.xml", language.GetPointer(), country.GetPointer());
173         SysTryReturn(NID_APP, !IsFailed(r), E_FAILURE, r, "[%s] Failed to format path string", GetErrorMessage(r));
174
175         const String& homePath = _AppInfo::GetAppRootPath();
176         __pDoc = ParseXmlFile( homePath + resFilename);
177
178         // Fall-back
179         if (__pDoc == null)
180         {
181                 String dirName(L"res/");
182                 Directory dir;
183                 DirEnumerator* pDirEnum = null;
184                 String name;
185
186                 r = dirName.Insert(homePath, 0);
187                 SysTryReturn(NID_APP, !IsFailed(r), E_FAILURE, r, "[%s] Failed to insert string.", GetErrorMessage(r));
188
189                 // open directory
190                 r = dir.Construct(dirName);
191                 if (r == E_SUCCESS)
192                 {
193                         // read all directory entries
194                         pDirEnum = dir.ReadN();
195                         if (pDirEnum != null)
196                         {
197                                 const String starts = language + L"-";
198                                 const String ends = L".xml";
199
200                                 // loop through all directory entries
201                                 while (pDirEnum->MoveNext() == E_SUCCESS)
202                                 {
203                                         // get several properties of each directory entry.
204                                         name = pDirEnum->GetCurrentDirEntry().GetName();
205
206                                         if (name.StartsWith(starts, 0) && name.EndsWith(ends))
207                                         {
208                                                 resFilename = L"res/" + name;
209                                                 __pDoc = ParseXmlFile(homePath + resFilename);
210                                                 break;
211                                         }
212                                 }
213                                 // Delete enumerator
214                                 delete pDirEnum;
215                         }
216                 }
217         }
218
219         if (__pDoc == null)
220         {
221                 __pDoc = ParseXmlFile(homePath + L"res/eng-GB.xml");
222         }
223
224         if (__pDoc == null)
225         {
226                 __pDoc = ParseXmlFile(homePath + L"res/eng-US.xml");
227         }
228
229         if (__pDoc == null)
230         {
231                 __pDoc = ParseXmlFile(homePath + L"res/English.xml");
232         }
233
234         if (__pDoc == null)
235         {
236                 SysLog(NID_APP, "Can't find xml resource file.");
237                 return E_SUCCESS; //E_FILE_NOT_FOUND;
238         }
239
240         xmlNodePtr cur = xmlDocGetRootElement(__pDoc);
241         SysTryReturnResult(NID_APP, cur != null, E_FAILURE, "Empty document.");
242
243         if (xmlStrcmp(cur->name, (const xmlChar*) AR_ROOT_NODE_NAME))
244         {
245                 SysLogException(NID_APP, E_FAILURE, "[E_FAILURE] Document root node is not <string_table>.");
246                 return E_FAILURE;
247         }
248
249         gCur = cur;
250
251         return E_SUCCESS;
252 }
253
254 result
255 _AppResourceString::InitializeStringInfo(const AppId& appId)
256 {
257         if (__pDoc != null)
258         {
259                 SysLog(NID_APP, "__pDoc is freed to reinitialize.");
260                 xmlFreeDoc(__pDoc);
261                 __pDoc = null;
262         }
263
264         char* pValue = null;
265         int ret = runtime_info_get_value_string(RUNTIME_INFO_KEY_LANGUAGE, &pValue);
266         SysTryReturnResult(NID_APP, ret == 0, E_FAILURE, "[E_SYSTEM] runtime_info_get_value_string returns %d.", ret);
267
268
269         U_ICU_NAMESPACE::Locale icuLcl(pValue);
270
271         String language(icuLcl.getISO3Language());
272         String country(icuLcl.getCountry());
273
274 //      SysLog(NID_APP, "res/%ls-%ls.xml", language.GetPointer(), country.GetPointer());
275
276         String resFilename;
277         result r = resFilename.Format(256 * 2 + 10, L"/res/%ls-%ls.xml", language.GetPointer(), country.GetPointer());
278         SysTryReturn(NID_APP, !IsFailed(r), E_FAILURE, r, "[%s] Failed to format path string", GetErrorMessage(r));
279
280         PackageInfo* pPkgInfo = null;
281         pPkgInfo = _PackageManagerImpl::GetInstance()->GetPackageInfoN(appId);
282         SysTryReturnResult(NID_APP, pPkgInfo != null, E_APP_NOT_INSTALLED, "Failed to get the package info");
283
284         _PackageInfoImpl* pPkgInfoImpl = _PackageInfoImpl::GetInstance(pPkgInfo);
285         SysTryReturnResult(NID_APP, pPkgInfoImpl != null, E_APP_NOT_INSTALLED, "Failed to get the package info impl");
286
287         const Tizen::Base::String& homePath = pPkgInfoImpl->GetAppRootPath();
288
289         __pDoc = ParseXmlFile( homePath + resFilename);
290
291         // Fall-back
292         if (__pDoc == null)
293         {
294                 String dirName(L"/res/");
295                 Directory dir;
296                 DirEnumerator* pDirEnum = null;
297                 String name;
298
299                 r = dirName.Insert(homePath, 0);
300                 SysTryReturn(NID_APP, !IsFailed(r), E_FAILURE, r, "[%s] Failed to insert string.", GetErrorMessage(r));
301
302                 // open directory
303                 r = dir.Construct(dirName);
304                 if (r == E_SUCCESS)
305                 {
306                         // read all directory entries
307                         pDirEnum = dir.ReadN();
308                         if (pDirEnum != null)
309                         {
310                                 const String starts = language + L"-";
311                                 const String ends = L".xml";
312
313                                 // loop through all directory entries
314                                 while (pDirEnum->MoveNext() == E_SUCCESS)
315                                 {
316                                         // get several properties of each directory entry.
317                                         name = pDirEnum->GetCurrentDirEntry().GetName();
318
319                                         if (name.StartsWith(starts, 0) && name.EndsWith(ends))
320                                         {
321                                                 resFilename = L"/res/" + name;
322                                                 __pDoc = ParseXmlFile(homePath + resFilename);
323                                                 break;
324                                         }
325                                 }
326                                 // Delete enumerator
327                                 delete pDirEnum;
328                         }
329                 }
330         }
331
332         if (__pDoc == null)
333         {
334                 __pDoc = ParseXmlFile(homePath + L"/res/eng-GB.xml");
335         }
336
337         if (__pDoc == null)
338         {
339                 __pDoc = ParseXmlFile(homePath + L"/res/eng-US.xml");
340         }
341
342         if (__pDoc == null)
343         {
344                 __pDoc = ParseXmlFile(homePath + L"/res/English.xml");
345         }
346
347         if (__pDoc == null)
348         {
349                 SysLog(NID_APP, "Can't find xml resource file.");
350                 return E_DATA_NOT_FOUND;
351         }
352
353         xmlNodePtr cur = xmlDocGetRootElement(__pDoc);
354         SysTryReturnResult(NID_APP, cur != null, E_FAILURE, "Empty document.");
355
356         if (xmlStrcmp(cur->name, (const xmlChar*) AR_ROOT_NODE_NAME))
357         {
358                 SysLogException(NID_APP, E_FAILURE, "[E_FAILURE] Document root node is not <string_table>.");
359                 return E_FAILURE;
360         }
361
362         gCur = cur;
363
364         delete pPkgInfo;
365         return E_SUCCESS;
366 }
367
368 xmlDocPtr
369 _AppResourceString::ParseXmlFile(const String& path)
370 {
371         if (File::IsFileExist(path) == false )
372         {
373                 SysLog(NID_APP, "'%ls' isn't exist.", path.GetPointer());
374                 return null;
375         }
376
377         std::unique_ptr<char[]> pBuf(_StringConverter::CopyToCharArrayN(path));
378         xmlDocPtr docPtr = xmlParseFile(pBuf.get());
379
380         if (docPtr == null)
381         {
382                 SysLog(NID_APP, "failed to parse resource file(%ls)", path.GetPointer());
383                 return null;
384         }
385
386         SysLog(NID_APP, "'%ls' is parsed successfully.", path.GetPointer());
387         return docPtr;
388 }
389
390
391 _AppResourceString::~_AppResourceString(void)
392 {
393         if (__pDoc != null)
394         {
395                 xmlFreeDoc(__pDoc);
396         }
397         delete __pLockOfParser;
398 }
399
400
401 result
402 _AppResourceString::GetString(const String resourceId, String& loadedString)
403 {
404         SysTryReturnResult(NID_APP, __pDoc != null, E_INVALID_STATE, "Can't find xml resource file.");
405
406 #if defined(ENABLE_XPATH)
407         xmlXPathContextPtr __pXpathCtx = null;
408         xmlXPathObjectPtr __pXpathObj = null;
409
410         String key;
411         key.Format(1024, L"/string_table/text[@id='%ls']", resourceId.GetPointer());
412
413         xmlChar* pXpathExpr = (xmlChar*) (_StringConverter::CopyToCharArrayN(key));
414         xmlChar* pContent = null;
415         // Create xpath evaluation context
416         __pXpathCtx = xmlXPathNewContext(__pDoc);
417         if (__pXpathCtx == null)
418         {
419                 SysLogException(NID_APP, E_INVALID_STATE, "[E_INVALID_STATE] XPathNewContext must not be null.");
420
421                 delete[] pXpathExpr;
422                 return E_INVALID_STATE;
423         }
424
425         // Evaluate xpath expression
426         __pXpathObj = xmlXPathEvalExpression(pXpathExpr, __pXpathCtx);
427         if (null == __pXpathObj ||
428                 null == __pXpathObj->nodesetval ||
429                 null == __pXpathObj->nodesetval->nodeTab ||
430                 null == __pXpathObj->nodesetval->nodeTab[0]->children
431                 )
432         {
433                 SysLog(NID_APP, "xpath expression is invalid.");
434
435                 xmlXPathFreeContext(__pXpathCtx);
436                 __pXpathCtx = null;
437                 delete[] pXpathExpr;
438                 return E_INVALID_ARG;
439         }
440         delete[] pXpathExpr;
441
442         pContent = __pXpathObj->nodesetval->nodeTab[0]->children->content;
443         loadedString = String((char*) pContent);
444
445         SysLog(NID_APP, "content : %s.", pContent);
446
447         // file close & open
448         // Cleanup of XPath data
449         xmlXPathFreeObject(__pXpathObj);
450         __pXpathObj = null;
451         xmlXPathFreeContext(__pXpathCtx);
452         __pXpathCtx = null;
453
454         SysLog(NID_APP, "Free xpath data.");
455
456         return E_SUCCESS;
457
458 #else
459         xmlNodePtr cur = null;
460         xmlChar* pValue = null;
461         xmlChar* pId = null;
462
463         SysTryReturnResult(NID_APP, !resourceId.IsEmpty(), E_INVALID_ARG, "Wrong resource Id.");
464         SysTryReturnResult(NID_APP, gCur != null && __pDoc != null, E_INVALID_STATE,
465                                 "Application string resource was not initialized.");
466         SysTryReturnResult(NID_APP, __pLockOfParser != null, E_INVALID_STATE, "__pLockOfParser was not initialized.");
467
468         result r = __pLockOfParser->Acquire();
469         SysTryReturn(NID_APP, !IsFailed(r), r, r, "[%s] Failed to Acquire Mutex.", GetErrorMessage(r));
470
471         pId = (xmlChar*) (_StringConverter::CopyToCharArrayN(resourceId));
472         cur = gCur;
473         cur = cur->xmlChildrenNode;
474         while (cur != null)
475         {
476                 if ((!xmlStrcmp(cur->name, (const xmlChar*) AR_CHILD_NODE_1)))
477                 {
478                         pValue = xmlGetProp(cur, (const xmlChar*) AR_ATTRIBUTE);
479                         if (!xmlStrcmp(pValue, (const xmlChar*) pId))
480                         {
481                                 xmlChar* pContent = xmlNodeListGetString( __pDoc, cur->xmlChildrenNode, 1);
482                                 SysLog(NID_APP, "xml content=%s", pContent);
483                                 loadedString = (char*) (pContent);
484
485                                 if (HasSpecialString(loadedString))
486                                 {
487                                         String rawString(loadedString);
488                                         ConvertToCstyleString(rawString, loadedString);
489                                 }
490
491                                 if (pContent)
492                                 {
493                                         xmlFree(pContent);
494                                 }
495
496                                 if (pValue)
497                                 {
498                                         xmlFree(pValue);
499                                 }
500
501                                 if (pId)
502                                 {
503                                         delete[] (char*) pId;
504                                 }
505
506                                 __pLockOfParser->Release();
507                                 return E_SUCCESS;
508                         }
509                         if (pValue)
510                         {
511                                 xmlFree(pValue);
512                         }
513                 }
514                 cur = cur->next;
515         }
516
517         if (pId)
518         {
519                 delete[] (char*) pId;
520         }
521
522         __pLockOfParser->Release();
523
524         return E_FAILURE;
525 #endif
526 }
527
528
529 bool
530 _AppResourceString::HasSpecialString(const String& resourceStr)
531 {
532         int foundIndex = 0;
533
534         return((resourceStr.IndexOf("&lt;", 0, foundIndex) == E_SUCCESS)
535                 || (resourceStr.IndexOf("&amp;", 0, foundIndex) == E_SUCCESS)
536                 || (resourceStr.IndexOf("&gt;", 0, foundIndex) == E_SUCCESS)
537                 || (resourceStr.IndexOf("&apos;", 0, foundIndex) == E_SUCCESS)
538                 || (resourceStr.IndexOf("&quot", 0, foundIndex) == E_SUCCESS)
539                 || (resourceStr.IndexOf("&amp;", 0, foundIndex) == E_SUCCESS)
540                 || (resourceStr.IndexOf("\\", 0, foundIndex) == E_SUCCESS));
541 }
542
543
544 result
545 _AppResourceString::ConvertToCstyleString(const String& resourceStr, String& convertedStr)
546 {
547         result r = E_SUCCESS;
548
549         int i = 0;
550         int j = 0;
551         int len = 0;
552
553         SysTryReturnResult(NID_APP, resourceStr.GetLength() > 0, E_INVALID_ARG, "resource string must be greater than 0.");
554
555         std::unique_ptr<char[]> pBuffer(_StringConverter::CopyToCharArrayN(resourceStr));
556         // len = resourceStr.GetLength(); comment out because GetLength() returns UTF8 length.
557         SysTryReturnResult(NID_APP, pBuffer != null, E_OUT_OF_MEMORY, "Memory allocation failure.");
558
559         len = strlen(pBuffer.get());//  len = String_length((MString)pBuffer);
560
561         while (i < len)
562         {
563                 if (pBuffer[i] == '\\')
564                 {
565                         if (pBuffer[i + 1] == 'n')
566                         {
567                                 pBuffer[j] = '\n';
568                                 i += 2;
569                                 j++;
570                         }
571                         else if (pBuffer[i + 1] == 't')
572                         {
573                                 pBuffer[j] = '\t';
574                                 i += 2;
575                                 j++;
576                         }
577                         else if (pBuffer[i + 1] == '\\')
578                         {
579                                 pBuffer[j] = '\\';
580                                 i += 2;
581                                 j++;
582                         }
583                         else
584                         {
585                                 i++;
586                         }
587                 }
588                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'l') && (pBuffer[i + 2] == 't') &&
589                                  (pBuffer[i + 3] == ';'))
590                 {
591                         pBuffer[j] = '<';
592                         i += 4;
593                         j++;
594                 }
595                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'g') && (pBuffer[i + 2] == 't') &&
596                                  (pBuffer[i + 3] == ';'))
597                 {
598                         pBuffer[j] = '>';
599                         i += 4;
600                         j++;
601                 }
602                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'a') && (pBuffer[i + 2] == 'm') &&
603                                  (pBuffer[i + 3] == 'p') && (pBuffer[i + 4] == ';'))
604                 {
605                         pBuffer[j] = '&';
606                         i += 5;
607                         j++;
608                 }
609                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'a') && (pBuffer[i + 2] == 'p') &&
610                                  (pBuffer[i + 3] == 'o') && (pBuffer[i + 4] == 's') && (pBuffer[i + 5] == ';'))
611                 {
612                         pBuffer[j] = 39; // '
613                         i += 6;
614                         j++;
615                 }
616                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'q') && (pBuffer[i + 2] == 'u') &&
617                                  (pBuffer[i + 3] == 'o') && (pBuffer[i + 4] == 't') && (pBuffer[i + 5] == ';'))
618                 {
619                         pBuffer[j] = 34; // "
620                         i += 6;
621                         j++;
622                 }
623                 else
624                 {
625                         pBuffer[j] = pBuffer[i];
626                         i++;
627                         j++;
628                 }
629         }
630         if (j != i)
631         {
632                 for (; j <= i; j++)
633                 {
634                         pBuffer[j] = '\0';
635                 }
636         }
637
638         convertedStr = pBuffer.get();
639
640         return r;
641 }
642
643
644 } } // Tizen::App