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