tizen 2.4 release
[framework/web/wrt-commons.git] / modules / localization / src / w3c_file_localization.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file    w3c_file_localization.cpp
18  * @author  Lukasz Wrzosek (l.wrzosek@samsung.com)
19  * @version 1.0
20  */
21 #include <stddef.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <assert.h>
26
27 #include <dpl/localization/w3c_file_localization.h>
28
29 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
30 #include <dpl/localization/localization_utils.h>
31
32 #include <dpl/log/wrt_log.h>
33 #include <dpl/string.h>
34 #include <dpl/foreach.h>
35
36 #include <LanguageTagsProvider.h>
37
38 using namespace WrtDB;
39
40 namespace {
41 const DPL::String FILE_URI_BEGIN = L"file://";
42 const DPL::String WIDGET_URI_BEGIN = L"widget://";
43 const DPL::String APP_URI_BEGIN = L"app://";
44 const DPL::String LOCALE_PREFIX = L"locales/";
45
46 DPL::OptionalStdString GetFilePathInWidgetPackageInternal(
47     const std::string& basePath,
48     std::string filePath)
49 {
50     WrtLogD("Looking for file: %s in: %s", filePath.c_str(), basePath.c_str());
51
52     const LanguageTags& ltags =
53         LanguageTagsProviderSingleton::Instance().getLanguageTags();
54
55     //Check if string isn't empty
56     if (filePath.size() == 0) {
57         return DPL::OptionalStdString();
58     }
59     //Removing preceding '/'
60     if (filePath[0] == '/') {
61         filePath.erase(0, 1);
62     }
63     // In some cases (start file localization) url has unnecessary "/" at the
64     // end
65     if (filePath[filePath.size() - 1] == '/') {
66         filePath.erase(filePath.size() - 1, 1);
67     }
68     //Check if string isn't empty
69     if (filePath.size() == 0) {
70         return DPL::OptionalStdString();
71     }
72
73     WrtLogD("locales size = %i", ltags.size());
74     for (LanguageTags::const_iterator it = ltags.begin();
75          it != ltags.end();
76          ++it)
77     {
78         WrtLogD("Trying locale: %ls", it->c_str());
79         std::string path = basePath;
80         if (path[path.size() - 1] == '/') {
81             path.erase(path.size() - 1);
82         }
83
84         if (it->empty()) {
85             path += "/" + filePath;
86         } else {
87             path += "/locales/" + DPL::ToUTF8String(*it) + "/" + filePath;
88         }
89
90         WrtLogD("Trying locale: %ls | %s", it->c_str(), path.c_str());
91         struct stat buf;
92         if (0 == stat(path.c_str(), &buf)) {
93             if ((buf.st_mode & S_IFMT) == S_IFREG) {
94                 path.erase(0, basePath.length());
95                 return DPL::OptionalStdString(path);
96             }
97         }
98     }
99
100     return DPL::OptionalStdString();
101 }
102
103 DPL::OptionalString GetFilePathInWidgetPackageInternal(
104     const DPL::String& basePath,
105     const DPL::String& filePath)
106 {
107     DPL::OptionalStdString path =
108         GetFilePathInWidgetPackageInternal(DPL::ToUTF8String(basePath),
109                                            DPL::ToUTF8String(filePath));
110     DPL::OptionalString dplPath;
111     if (!!path) {
112         dplPath = DPL::FromUTF8String(*path);
113     }
114     return dplPath;
115 }
116 }
117
118 namespace W3CFileLocalization {
119 static bool isExistFileCached(const std::string& path)
120 {
121     static std::map<std::string, bool> pathCache;
122
123     // is cached?
124     if (pathCache.find(path) == pathCache.end())
125     {
126         struct stat buf;
127
128         if (0 == stat(path.c_str(), &buf))
129         {
130             pathCache[path] = true;
131
132             return true;
133         }
134         else
135         {
136             pathCache[path] = false;
137
138             return false;
139         }
140     }
141
142     return pathCache[path];
143 }
144
145 std::string getFilePathInWidgetPackageFromUrl(const std::string &tzAppId, const std::string &url)
146 {
147     const std::string SCHEME_FILE   = "file://";
148     const std::string SCHEME_WIDGET = "widget://";
149     const std::string SCHEM_APP     = "app://";
150     const std::string LOCALE_PATH   = "locales/";
151     const std::string DOUBLE_ROOT   = "//";
152
153     static std::string          lastTzAppId;
154     static WidgetDAOReadOnlyPtr dao;
155     static std::string          srcPath;
156
157     std::string workingUrl = url;
158     bool        found = false;
159
160     // Dao caching
161     if (lastTzAppId != tzAppId)
162     {
163         lastTzAppId = tzAppId;
164         dao.reset(new WidgetDAOReadOnly(DPL::FromUTF8String(tzAppId)));
165         srcPath = DPL::ToUTF8String(dao->getPath());
166     }
167
168     // backup suffix string
169     std::string backupSuffix;
170     size_t pos = workingUrl.find_first_of("#?");
171
172     if (pos != std::string::npos)
173     {
174         backupSuffix = workingUrl.substr(pos);
175         workingUrl.resize(pos);
176     }
177
178     // make basis path
179     if (workingUrl.compare(0, SCHEME_WIDGET.length(), SCHEME_WIDGET) == 0)
180     {
181         // remove "widget://"
182         workingUrl.erase(0, SCHEME_WIDGET.length());
183     }
184     else if (workingUrl.compare(0, SCHEME_FILE.length(), SCHEME_FILE) == 0)
185     {
186         // remove "file://"
187         workingUrl.erase(0, SCHEME_FILE.length());
188
189         // exception handling for "//"
190         if (workingUrl.compare(0, DOUBLE_ROOT.length(), DOUBLE_ROOT) == 0)
191         {
192             workingUrl.erase(0, 1);
193             WrtLogD("workingUrl: %s", workingUrl.c_str());
194         }
195
196         // remove src path
197         if (workingUrl.compare(0, srcPath.length(), srcPath) == 0)
198         {
199             workingUrl.erase(0, srcPath.length());
200         }
201
202         // remove locale path
203         if (workingUrl.compare(0, LOCALE_PATH.length(), LOCALE_PATH) == 0)
204         {
205             workingUrl.erase(0, LOCALE_PATH.length());
206
207             pos = workingUrl.find_first_of('/');
208
209             if (pos != std::string::npos && pos > 0)
210             {
211                 workingUrl.erase(0, pos+1);
212             }
213         }
214     }
215     else if (workingUrl.compare(0, SCHEM_APP.length(), SCHEM_APP) == 0)
216     {
217         // remove "app://"
218         workingUrl.erase(0, SCHEM_APP.length());
219
220         // remove tizen app id
221         if (workingUrl.compare(0, tzAppId.length(), tzAppId) == 0)
222         {
223             workingUrl.erase(0, tzAppId.length());
224         }
225         else
226         {
227             WrtLogE("Tizen id does not match, ignoring");
228             return "";
229         }
230     }
231
232     // remove '/' token
233     if (!workingUrl.empty() && workingUrl[0] == '/')
234     {
235         workingUrl.erase(0, 1);
236     }
237
238     if (!workingUrl.empty() && workingUrl[workingUrl.length()-1] == '/') {
239         workingUrl.erase(workingUrl.length()-1, 1);
240     }
241
242     if (workingUrl.empty())
243     {
244         WrtLogE("URL Localization Error!");
245         return "";
246     }
247
248     const LanguageTags& ltags = LanguageTagsProviderSingleton::Instance().getLanguageTags();
249
250     FOREACH(it, ltags)
251     {
252         std::string path = srcPath;
253
254         if (!it->empty())
255         {
256             path += LOCALE_PATH;
257
258             if (isExistFileCached(path) == false)
259             {
260                 continue;
261             }
262
263             path += DPL::ToUTF8String(*it) + "/";
264
265             if (isExistFileCached(path) == false)
266             {
267                 continue;
268             }
269         }
270
271         path += workingUrl;
272
273         if (isExistFileCached(path) == true)
274         {
275             found = true;
276             workingUrl = path;
277             break;
278         }
279     }
280
281     // restore suffix string
282     if (!found)
283     {
284         // return empty string
285         workingUrl = "";
286     }
287     else
288     {
289         if (!backupSuffix.empty())
290         {
291             workingUrl += backupSuffix;
292         }
293     }
294
295     return workingUrl;
296 }
297
298 DPL::OptionalString getFilePathInWidgetPackageFromUrl(
299     const WrtDB::TizenAppId &tzAppId,
300     const DPL::String &url)
301 {
302     return getFilePathInWidgetPackageFromUrl(
303                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)),
304                url);
305 }
306
307 DPL::OptionalString getFilePathInWidgetPackageFromUrl(
308     WrtDB::WidgetDAOReadOnlyPtr dao,
309     const DPL::String &url)
310 {
311     DPL::String req = url;
312
313     DPL::String suffix;
314     DPL::String::size_type pos = req.find_first_of('#');
315     if(pos != DPL::String::npos)
316     {
317         suffix = req.substr(pos) + suffix;
318         req.resize(pos); //truncate fragment identifier
319     }
320
321     pos = req.find_first_of('?');
322     if(pos != DPL::String::npos)
323     {
324         suffix = req.substr(pos) + suffix;
325         req.resize(pos); //truncate query string
326     }
327
328     if (req.find(WIDGET_URI_BEGIN) == 0) {
329         req.erase(0, WIDGET_URI_BEGIN.length());
330     } else if (req.find(FILE_URI_BEGIN) == 0) {
331         req.erase(0, FILE_URI_BEGIN.length());
332         if (req.find(dao->getPath()) == 0) {
333             req.erase(0, dao->getPath().length());
334         }
335         if (req.find(LOCALE_PREFIX) == 0) {
336             req.erase(0, LOCALE_PREFIX.length());
337             size_t position = req.find('/');
338             // should always be >0 as correct locales path is
339             // always locales/xx/ or locales/xx-XX/
340             if (position != std::string::npos && position > 0) {
341                 req.erase(0, position + 1);
342             }
343         }
344     } else if(req.find(APP_URI_BEGIN) == 0) {
345         req.erase(0, APP_URI_BEGIN.length());
346         DPL::String id = dao->getTizenAppId();
347         if(req.substr(0, id.size()) != id)
348         {
349             WrtLogE("Tizen id does not match, ignoring");
350             return DPL::OptionalString();
351         }
352         req.erase(0, id.length());
353     } else {
354         WrtLogD("Unknown path format, ignoring");
355         return DPL::OptionalString();
356     }
357
358     auto widgetPath = dao->getPath();
359
360     WrtLogD("Required path: %ls", req.c_str());
361     DPL::OptionalString found =
362         GetFilePathInWidgetPackageInternal(widgetPath, req);
363
364     if (!found) {
365         WrtLogE("Path not found within current locale in current widget");
366         return DPL::OptionalString();
367     }
368
369     found = widgetPath + *found + suffix;
370
371     return found;
372 }
373
374 DPL::OptionalString getFilePathInWidgetPackage(
375     const WrtDB::TizenAppId &tzAppId,
376     const DPL::String& file)
377 {
378     return getFilePathInWidgetPackage(
379                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)),
380                file);
381 }
382
383 DPL::OptionalString getFilePathInWidgetPackage(
384     WrtDB::WidgetDAOReadOnlyPtr dao,
385     const DPL::String& file)
386 {
387     return GetFilePathInWidgetPackageInternal(dao->getPath(), file);
388 }
389
390 DPL::OptionalString getStartFile(const WrtDB::TizenAppId & tzAppId)
391 {
392     return getStartFile(WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
393 }
394
395 DPL::OptionalString getStartFile(WrtDB::WidgetDAOReadOnlyPtr dao)
396 {
397     WidgetDAOReadOnly::LocalizedStartFileList locList =
398         dao->getLocalizedStartFileList();
399     WidgetDAOReadOnly::WidgetStartFileList list = dao->getStartFileList();
400     LanguageTags tagsList =
401         LanguageTagsProviderSingleton::Instance().getLanguageTags();
402
403     DPL::OptionalString defaultLoc = dao->getDefaultlocale();
404     if (!!defaultLoc) {
405         tagsList.push_back(*defaultLoc);
406     }
407
408     FOREACH(tag, tagsList)
409     {
410         FOREACH(sFile, locList)
411         {
412             if (*tag == sFile->widgetLocale) {
413                 FOREACH(it, list)
414                 {
415                     if (it->startFileId == sFile->startFileId) {
416                         return it->src;
417                     }
418                 }
419             }
420         }
421     }
422
423     return DPL::OptionalString();
424 }
425
426 OptionalWidgetIcon getIcon(const WrtDB::TizenAppId & tzAppId)
427 {
428     return getIcon(WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
429 }
430
431 OptionalWidgetIcon getIcon(WrtDB::WidgetDAOReadOnlyPtr dao)
432 {
433     WidgetDAOReadOnly::WidgetLocalizedIconList locList =
434         dao->getLocalizedIconList();
435     WidgetDAOReadOnly::WidgetIconList list = dao->getIconList();
436     LanguageTags tagsList =
437         LanguageTagsProviderSingleton::Instance().getLanguageTags();
438
439     DPL::OptionalString defaultLoc = dao->getDefaultlocale();
440     if (!!defaultLoc) {
441         tagsList.push_back(*defaultLoc);
442     }
443
444     FOREACH(tag, tagsList)
445     {
446         FOREACH(icon, locList)
447         {
448             if (*tag == icon->widgetLocale) {
449                 FOREACH(it, list)
450                 {
451                     if (it->iconId == icon->iconId) {
452                         WidgetIcon ret;
453                         ret.src = it->iconSrc;
454                         ret.width = it->iconWidth;
455                         ret.height = it->iconHeight;
456                         return ret;
457                     }
458                 }
459             }
460         }
461     }
462
463     return OptionalWidgetIcon();
464 }
465
466 WidgetIconList getValidIconsList(const WrtDB::TizenAppId &tzAppId)
467 {
468     return getValidIconsList(
469                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
470 }
471
472 WidgetIconList getValidIconsList(WrtDB::WidgetDAOReadOnlyPtr dao)
473 {
474     WidgetDAOReadOnly::WidgetIconList list = dao->getIconList();
475
476     WidgetIconList outlist;
477
478     FOREACH(it, list)
479     {
480         WrtLogD(":%ls", it->iconSrc.c_str());
481         if (!!getFilePathInWidgetPackage(dao->getTizenAppId(),
482                                          it->iconSrc))
483         {
484             WidgetIcon ret;
485             ret.src = it->iconSrc;
486             ret.width = it->iconWidth;
487             ret.height = it->iconHeight;
488             outlist.push_back(ret);
489         }
490     }
491     return outlist;
492 }
493
494 OptionalWidgetStartFileInfo getStartFileInfo(
495     const WrtDB::TizenAppId &tzAppId)
496 {
497     return getStartFileInfo(
498                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
499 }
500
501 OptionalWidgetStartFileInfo getStartFileInfo(WrtDB::WidgetDAOReadOnlyPtr dao)
502 {
503     WidgetStartFileInfo info;
504
505     WidgetDAOReadOnly::LocalizedStartFileList locList =
506         dao->getLocalizedStartFileList();
507     WidgetDAOReadOnly::WidgetStartFileList list = dao->getStartFileList();
508     const LanguageTags tagsList =
509         LanguageTagsProviderSingleton::Instance().getLanguageTags();
510
511     FOREACH(tag, tagsList)
512     {
513         FOREACH(sFile, locList)
514         {
515             if (*tag == sFile->widgetLocale) {
516                 FOREACH(it, list)
517                 {
518                     if (it->startFileId ==
519                         sFile->startFileId)
520                     {
521                         info.file = it->src;
522                         info.encoding = sFile->encoding;
523                         info.type = sFile->type;
524                         if (tag->empty()) {
525                             info.localizedPath = it->src;
526                         } else {
527                             info.localizedPath = L"locales/" + *tag + L"/";
528                             info.localizedPath += it->src;
529                         }
530                         return info;
531                     }
532                 }
533             }
534         }
535     }
536
537     return OptionalWidgetStartFileInfo();
538 }
539
540 WidgetLocalizedInfo getLocalizedInfo(const WrtDB::TizenAppId & tzAppId)
541 {
542     return getLocalizedInfo(WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
543 }
544
545 WidgetLocalizedInfo getLocalizedInfo(WidgetDAOReadOnlyPtr dao)
546 {
547     LanguageTags languages =
548         LanguageTagsProviderSingleton::Instance().getLanguageTags();
549     DPL::OptionalString dl = dao->getDefaultlocale();
550     if (!!dl) {
551         languages.push_back(*dl);
552     }
553
554     WidgetLocalizedInfo result;
555     FOREACH(i, languages)
556     {
557         WidgetLocalizedInfo languageResult = dao->getLocalizedInfo(*i);
558
559 #define OVERWRITE_IF_NULL(FIELD) if (!result.FIELD) { \
560         result.FIELD = languageResult.FIELD; \
561 }
562
563         OVERWRITE_IF_NULL(name);
564         OVERWRITE_IF_NULL(shortName);
565         OVERWRITE_IF_NULL(description);
566         OVERWRITE_IF_NULL(license);
567         OVERWRITE_IF_NULL(licenseHref);
568
569 #undef OVERWRITE_IF_NULL
570     }
571
572     return result;
573 }
574 }