Merge "Change the way to conver Mbs to Wcs and vice versa" into tizen_2.1
[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 <unicode/locid.h>
27
28 #include <unique_ptr.h>
29 #include <runtime_info.h>
30
31 #include <FBaseResult.h>
32 #include <FBaseSysLog.h>
33 #include <FBase_StringConverter.h>
34 #include <FBaseColHashMap.h>
35
36 #include <FIoFile.h>
37 #include <FIoDirectory.h>
38
39 #include <FApp_AppInfo.h>
40 #include <FAppPkgPackageInfo.h>
41 #include "FAppPkg_PackageManagerImpl.h"
42 #include "FAppPkg_PackageInfoImpl.h"
43
44 #include "FApp_AppResourceImpl.h"
45 #include "FApp_AppResourceString.h"
46
47 using namespace Tizen::App::Package;
48 using namespace Tizen::Base;
49 using namespace Tizen::Base::Collection;
50 using namespace Tizen::Io;
51
52 static const char* AR_ROOT_NODE_NAME = "string_table";
53 static const char* AR_CHILD_NODE_1 = "text";
54 static const char* AR_ATTRIBUTE = "id";
55
56 struct FreeXmlDoc
57 {
58         void operator ()(_xmlDoc* p)
59         {
60                 if (p != null)
61                 {
62                         xmlFreeDoc(p);
63                 }
64         }
65 };
66
67 namespace Tizen { namespace App
68 {
69
70 _AppResourceString::_AppResourceString(void)
71         : __pDoc(null)
72         , __pLockOfParser(null)
73         , __pXmlNodeMap(null)
74 {
75 }
76
77 _AppResourceString::~_AppResourceString(void)
78 {
79         if (__pDoc != null)
80         {
81                 xmlFreeDoc(__pDoc);
82         }
83         if (__pXmlNodeMap)
84         {
85                 __pXmlNodeMap->RemoveAll(true);
86                 delete __pXmlNodeMap;
87         }
88         delete __pLockOfParser;
89 }
90
91 // Exception: E_OUT_OF_MEMORY, E_APP_NOT_INSTALLED, E_SYSTEM
92 _AppResourceString*
93 _AppResourceString::Get_AppResourceStringN(int type, const Tizen::Base::String& value)
94 {
95         std::unique_ptr< _AppResourceString > pAppResourceString(new (std::nothrow) _AppResourceString);
96         SysTryReturn(NID_APP, pAppResourceString != null, null,
97                         E_OUT_OF_MEMORY, "[%s] Unable to allocate memory for _AppResourceString", GetErrorMessage(E_OUT_OF_MEMORY));
98
99         String resourceFolder;
100         switch(type)
101         {
102         case APP_RESOURCE_DEFAULT:
103         case APP_RESOURCE_BY_LIBRARY_NAME:
104         default:
105                 resourceFolder = _AppInfo::GetAppRootPath();
106                 resourceFolder.Append(L"res/");
107                 resourceFolder.Append(value);
108                 resourceFolder.Append(L"/");
109                 break;
110
111         case APP_RESOURCE_BY_APP_ID:
112                 std::unique_ptr< PackageInfo> pPkgInfo(_PackageManagerImpl::GetInstance()->GetPackageInfoN(value));
113                 SysTryReturn(NID_APP, pPkgInfo != null, null,
114                                 E_APP_NOT_INSTALLED, "[%s] Failed to get the package info", GetErrorMessage(E_APP_NOT_INSTALLED));
115
116                 _PackageInfoImpl* pPkgInfoImpl = _PackageInfoImpl::GetInstance(pPkgInfo.get());
117                 SysTryReturn(NID_APP, pPkgInfoImpl != null, null,
118                                 E_APP_NOT_INSTALLED, "[%s] Failed to get the package info impl", GetErrorMessage(E_APP_NOT_INSTALLED));
119
120                 resourceFolder = pPkgInfoImpl->GetAppRootPath();
121                 resourceFolder.Append(L"/res/");
122                 break;
123         }
124
125         result r = pAppResourceString->Initialize(resourceFolder);
126         SysTryReturn(NID_APP, !IsFailed(r), null,
127                                 r, "[%s] Failed to initialize AppResourceString", GetErrorMessage(r));
128
129         ClearLastResult();
130         return pAppResourceString.release();
131 }
132
133 result
134 _AppResourceString::Initialize(String& resourceFolder)
135 {
136         result r = E_SUCCESS;
137         if (__pLockOfParser == null)
138         {
139                 __pLockOfParser = new (std::nothrow) Tizen::Base::Runtime::Mutex();
140                 SysTryReturnResult(NID_APP, __pLockOfParser != null, E_OUT_OF_MEMORY, "Failed to initialize resource parser.");
141
142                 r = __pLockOfParser->Create();
143                 SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to Create Mutex.", GetErrorMessage(r));
144         }
145
146         r = __pLockOfParser->Acquire();
147         SysTryCatch(NID_APP, !IsFailed(r), r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Failed to Acquire Mutex [%s].", GetErrorMessage(r));
148
149         r = InitializeStringInfo(resourceFolder);
150         __pLockOfParser->Release();
151
152         SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to InitializeStringInfo.", GetErrorMessage(r));
153         return E_SUCCESS;
154
155 CATCH:
156         delete __pLockOfParser;
157         __pLockOfParser = null;
158
159         return r;
160 }
161
162 result
163 _AppResourceString::InitializeStringInfo(String& resourceFolder)
164 {
165         char* pVal = null;
166         int ret = runtime_info_get_value_string(RUNTIME_INFO_KEY_LANGUAGE, &pVal);
167         SysTryReturnResult(NID_APP, ret == 0, E_SYSTEM, "runtime_info_get_value_string returns %d.", ret);
168
169         U_ICU_NAMESPACE::Locale icuLcl(pVal);
170         String language(icuLcl.getISO3Language());
171         String country(icuLcl.getCountry());
172         if (pVal)
173         {
174                 free(pVal);
175         }
176
177         String resFilename;
178         result r = resFilename.Format(256 * 2 + 10, L"%ls-%ls.xml", language.GetPointer(), country.GetPointer());
179         SysTryReturn(NID_APP, !IsFailed(r), E_SYSTEM, r, "[E_SYSTEM] Failed to format path string [%s]", GetErrorMessage(r));
180
181         if (!resourceFolder.EndsWith(L'/'))
182         {
183                 resourceFolder.Append(L'/');
184         }
185
186         std::unique_ptr< _xmlDoc, FreeXmlDoc > pDoc(ParseXmlFile(resourceFolder + resFilename));
187         if (pDoc == null)
188         {
189                 // open directory
190                 Directory dir;
191                 r = dir.Construct(resourceFolder);
192                 if (r == E_SUCCESS)
193                 {
194                         // read all directory entries
195                         DirEnumerator* pDirEnum = dir.ReadN();
196                         if (pDirEnum != null)
197                         {
198                                 const String starts = language + L"-";
199                                 const String ends = L".xml";
200
201                                 // loop through all directory entries
202                                 while (pDirEnum->MoveNext() == E_SUCCESS)
203                                 {
204                                         // get several properties of each directory entry.
205                                         resFilename = pDirEnum->GetCurrentDirEntry().GetName();
206                                         if (resFilename.StartsWith(starts, 0) && resFilename.EndsWith(ends))
207                                         {
208                                                 pDoc.reset(ParseXmlFile(resourceFolder + resFilename));
209                                                 break;
210                                         }
211                                 }
212                                 // Delete enumerator
213                                 delete pDirEnum;
214                         }
215                 }
216         }
217
218         if (pDoc == null)
219         {
220                 pDoc.reset(ParseXmlFile(resourceFolder + L"eng-GB.xml"));
221         }
222
223         if (pDoc == null)
224         {
225                 pDoc.reset(ParseXmlFile(resourceFolder + L"eng-US.xml"));
226         }
227
228         if (pDoc == null)
229         {
230                 pDoc.reset(ParseXmlFile(resourceFolder + L"English.xml"));
231         }
232
233         SysTryLogReturn(NID_APP, pDoc != null, E_SUCCESS, "Can't find xml resource file.");
234
235         xmlNodePtr cur = xmlDocGetRootElement(pDoc.get());
236         SysTryReturnResult(NID_APP, cur != null, E_SYSTEM, "Empty document.");
237
238         ret = xmlStrcmp(cur->name, (const xmlChar*) AR_ROOT_NODE_NAME);
239         SysTryReturnResult(NID_APP, ret == 0, E_SYSTEM, "Document root node is not <string_table>.");
240
241         std::unique_ptr< HashMap, AllElementsDeleter > pXmlNodeMap (new (std::nothrow) HashMap());
242         SysTryReturnResult(NID_APP, pXmlNodeMap != null, E_OUT_OF_MEMORY, " Memory allocation failed.");
243
244         r = pXmlNodeMap->Construct();
245         SysTryReturnResult(NID_APP, !IsFailed(r), E_SYSTEM, "Unable to construct xml node map [%s]", GetErrorMessage(r));
246
247         xmlChar* pxmlValue = null;
248         cur = cur->xmlChildrenNode;
249         while (cur != null)
250         {
251                 if ((!xmlStrcmp(cur->name, (const xmlChar*) AR_CHILD_NODE_1)))
252                 {
253                         pxmlValue = xmlGetProp(cur, (const xmlChar*) AR_ATTRIBUTE);
254                         if (cur->type == (xmlElementType)  XML_ELEMENT_NODE)
255                         {
256                                 xmlChar* pContent = xmlNodeListGetString( pDoc.get(), cur->xmlChildrenNode, 1);
257
258                                 std::unique_ptr< String > pKey(new (std::nothrow) String);
259                                 SysTryReturnResult(NID_APP, pKey != null, E_OUT_OF_MEMORY, " Memory allocation failed.");
260                                 Tizen::Base::Utility::StringUtil::Utf8ToString((char*) pxmlValue, *pKey);
261
262                                 std::unique_ptr< String > pValue(new (std::nothrow) String);
263                                 SysTryReturnResult(NID_APP, pValue != null, E_OUT_OF_MEMORY, " Memory allocation failed.");
264                                 Tizen::Base::Utility::StringUtil::Utf8ToString((char*) pContent, *pValue);
265
266                                 pXmlNodeMap->Add(*pKey, *pValue);
267                                 pKey.release();
268                                 pValue.release();
269
270                                 if (pContent)
271                                 {
272                                         xmlFree(pContent);
273                                 }
274                         }
275
276                         if (pxmlValue)
277                         {
278                                 xmlFree(pxmlValue);
279                                 pxmlValue = null;
280                         }
281
282                 }
283                 cur =  cur->next;
284         }
285
286         if (__pDoc != null)
287         {
288                 xmlFreeDoc(__pDoc);
289                 __pDoc = null;
290         }
291
292         if (__pXmlNodeMap)
293         {
294                 __pXmlNodeMap->RemoveAll(true);
295                 delete __pXmlNodeMap;
296         }
297
298         __pDoc = pDoc.release();
299         __pXmlNodeMap = pXmlNodeMap.release();
300         return E_SUCCESS;
301 }
302
303 xmlDocPtr
304 _AppResourceString::ParseXmlFile(const String& path)
305 {
306         xmlDocPtr pDoc = null;
307         if (File::IsFileExist(path))
308         {
309                 std::unique_ptr<char[]> pBuf(_StringConverter::CopyToCharArrayN(path));
310                 pDoc = xmlParseFile(pBuf.get());
311         }
312         return pDoc;
313 }
314
315
316 result
317 _AppResourceString::GetString(const String resourceId, String& loadedString)
318 {
319 #if defined(ENABLE_XPATH)
320         SysTryReturnResult(NID_APP, __pDoc != null, E_FAILURE, "Can't find xml resource file.");
321
322         xmlXPathContextPtr __pXpathCtx = null;
323         xmlXPathObjectPtr __pXpathObj = null;
324
325         String key;
326         key.Format(1024, L"/string_table/text[@id='%ls']", resourceId.GetPointer());
327
328         xmlChar* pXpathExpr = (xmlChar*) (_StringConverter::CopyToCharArrayN(key));
329         xmlChar* pContent = null;
330         // Create xpath evaluation context
331         __pXpathCtx = xmlXPathNewContext(__pDoc);
332         if (__pXpathCtx == null)
333         {
334                 SysLogException(NID_APP, E_INVALID_STATE, "[E_INVALID_STATE] XPathNewContext must not be null.");
335
336                 delete[] pXpathExpr;
337                 return E_INVALID_STATE;
338         }
339
340         // Evaluate xpath expression
341         __pXpathObj = xmlXPathEvalExpression(pXpathExpr, __pXpathCtx);
342         if (null == __pXpathObj ||
343                 null == __pXpathObj->nodesetval ||
344                 null == __pXpathObj->nodesetval->nodeTab ||
345                 null == __pXpathObj->nodesetval->nodeTab[0]->children
346                 )
347         {
348                 SysLog(NID_APP, "xpath expression is invalid.");
349
350                 xmlXPathFreeContext(__pXpathCtx);
351                 __pXpathCtx = null;
352                 delete[] pXpathExpr;
353                 return E_INVALID_ARG;
354         }
355         delete[] pXpathExpr;
356
357         pContent = __pXpathObj->nodesetval->nodeTab[0]->children->content;
358         loadedString = String((char*) pContent);
359
360         SysLog(NID_APP, "content : %s.", pContent);
361
362         // file close & open
363         // Cleanup of XPath data
364         xmlXPathFreeObject(__pXpathObj);
365         __pXpathObj = null;
366         xmlXPathFreeContext(__pXpathCtx);
367         __pXpathCtx = null;
368
369         SysLog(NID_APP, "Free xpath data.");
370
371         return E_SUCCESS;
372
373 #else
374         SysTryReturnResult(NID_APP, !resourceId.IsEmpty(), E_INVALID_ARG, "Wrong resource Id.");
375         SysTryReturnResult(NID_APP, __pDoc != null, E_FAILURE, "Application string resource was not initialized.");
376         SysTryReturnResult(NID_APP, __pLockOfParser != null, E_FAILURE, "__pLockOfParser was not initialized.");
377
378         result r = __pLockOfParser->Acquire();
379         SysTryReturnResult(NID_APP, !IsFailed(r), E_FAILURE, "Failed to Acquire Mutex [%s].", GetErrorMessage(r));
380
381         String* pkeyValue = static_cast< String* > (__pXmlNodeMap->GetValue(resourceId));
382         r = E_FAILURE; //GetLastResult();
383         if (pkeyValue)
384         {
385                 loadedString = *pkeyValue;
386                 if (HasSpecialString(loadedString))
387                 {
388                         String rawString(loadedString);
389                         ConvertToCstyleString(rawString, loadedString);
390                 }
391                 r = E_SUCCESS;
392         }
393
394         __pLockOfParser->Release();
395         return r;
396 #endif
397 }
398
399
400 bool
401 _AppResourceString::HasSpecialString(const String& resourceStr)
402 {
403         int foundIndex = 0;
404         return((resourceStr.IndexOf("&lt;", 0, foundIndex) == E_SUCCESS)
405                 || (resourceStr.IndexOf("&amp;", 0, foundIndex) == E_SUCCESS)
406                 || (resourceStr.IndexOf("&gt;", 0, foundIndex) == E_SUCCESS)
407                 || (resourceStr.IndexOf("&apos;", 0, foundIndex) == E_SUCCESS)
408                 || (resourceStr.IndexOf("&quot", 0, foundIndex) == E_SUCCESS)
409                 || (resourceStr.IndexOf("&amp;", 0, foundIndex) == E_SUCCESS)
410                 || (resourceStr.IndexOf("\\", 0, foundIndex) == E_SUCCESS));
411 }
412
413
414 result
415 _AppResourceString::ConvertToCstyleString(const String& resourceStr, String& convertedStr)
416 {
417         SysTryReturnResult(NID_APP, resourceStr.GetLength() > 0, E_INVALID_ARG, "resource string must be greater than 0.");
418
419         std::unique_ptr<char[]> pBuffer(_StringConverter::CopyToCharArrayN(resourceStr));
420         SysTryReturnResult(NID_APP, pBuffer != null, E_OUT_OF_MEMORY, "Memory allocation failure.");
421
422         int i = 0;
423         int j = 0;
424         int len = strlen(pBuffer.get());//      len = String_length((MString)pBuffer);
425         while (i < len)
426         {
427                 if (pBuffer[i] == '\\')
428                 {
429                         if (pBuffer[i + 1] == 'n')
430                         {
431                                 pBuffer[j] = '\n';
432                                 i += 2;
433                                 j++;
434                         }
435                         else if (pBuffer[i + 1] == 't')
436                         {
437                                 pBuffer[j] = '\t';
438                                 i += 2;
439                                 j++;
440                         }
441                         else if (pBuffer[i + 1] == '\\')
442                         {
443                                 pBuffer[j] = '\\';
444                                 i += 2;
445                                 j++;
446                         }
447                         else
448                         {
449                                 i++;
450                         }
451                 }
452                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'l') && (pBuffer[i + 2] == 't') &&
453                                  (pBuffer[i + 3] == ';'))
454                 {
455                         pBuffer[j] = '<';
456                         i += 4;
457                         j++;
458                 }
459                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'g') && (pBuffer[i + 2] == 't') &&
460                                  (pBuffer[i + 3] == ';'))
461                 {
462                         pBuffer[j] = '>';
463                         i += 4;
464                         j++;
465                 }
466                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'a') && (pBuffer[i + 2] == 'm') &&
467                                  (pBuffer[i + 3] == 'p') && (pBuffer[i + 4] == ';'))
468                 {
469                         pBuffer[j] = '&';
470                         i += 5;
471                         j++;
472                 }
473                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'a') && (pBuffer[i + 2] == 'p') &&
474                                  (pBuffer[i + 3] == 'o') && (pBuffer[i + 4] == 's') && (pBuffer[i + 5] == ';'))
475                 {
476                         pBuffer[j] = 39; // '
477                         i += 6;
478                         j++;
479                 }
480                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'q') && (pBuffer[i + 2] == 'u') &&
481                                  (pBuffer[i + 3] == 'o') && (pBuffer[i + 4] == 't') && (pBuffer[i + 5] == ';'))
482                 {
483                         pBuffer[j] = 34; // "
484                         i += 6;
485                         j++;
486                 }
487                 else
488                 {
489                         pBuffer[j] = pBuffer[i];
490                         i++;
491                         j++;
492                 }
493         }
494         if (j != i)
495         {
496                 for (; j <= i; j++)
497                 {
498                         pBuffer[j] = '\0';
499                 }
500         }
501
502         convertedStr = pBuffer.get();
503         return E_SUCCESS;
504 }
505
506
507 } } // Tizen::App