Initialize Tizen 2.3
[framework/web/wrt-commons.git] / modules_wearable / 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     const WrtDB::TizenAppId &tzAppId,
301     const DPL::String &url)
302 {
303     return getFilePathInWidgetPackageFromUrl(
304                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)),
305                url);
306 }
307
308 DPL::Optional<DPL::String> getFilePathInWidgetPackageFromUrl(
309     WrtDB::WidgetDAOReadOnlyPtr dao,
310     const DPL::String &url)
311 {
312     DPL::String req = url;
313
314     DPL::String suffix;
315     DPL::String::size_type pos = req.find_first_of('#');
316     if(pos != DPL::String::npos)
317     {
318         suffix = req.substr(pos) + suffix;
319         req.resize(pos); //truncate fragment identifier
320     }
321
322     pos = req.find_first_of('?');
323     if(pos != DPL::String::npos)
324     {
325         suffix = req.substr(pos) + suffix;
326         req.resize(pos); //truncate query string
327     }
328
329     if (req.find(WIDGET_URI_BEGIN) == 0) {
330         req.erase(0, WIDGET_URI_BEGIN.length());
331     } else if (req.find(FILE_URI_BEGIN) == 0) {
332         req.erase(0, FILE_URI_BEGIN.length());
333         if (req.find(dao->getPath()) == 0) {
334             req.erase(0, dao->getPath().length());
335         }
336         if (req.find(LOCALE_PREFIX) == 0) {
337             req.erase(0, LOCALE_PREFIX.length());
338             size_t position = req.find('/');
339             // should always be >0 as correct locales path is
340             // always locales/xx/ or locales/xx-XX/
341             if (position != std::string::npos && position > 0) {
342                 req.erase(0, position + 1);
343             }
344         }
345     } else if(req.find(APP_URI_BEGIN) == 0) {
346         req.erase(0, APP_URI_BEGIN.length());
347         DPL::String id = dao->getTizenAppId();
348         if(req.substr(0, id.size()) != id)
349         {
350             LogError("Tizen id does not match, ignoring");
351             return DPL::Optional<DPL::String>::Null;
352         }
353         req.erase(0, id.length());
354     } else {
355         LogDebug("Unknown path format, ignoring");
356         return DPL::Optional<DPL::String>::Null;
357     }
358
359     auto widgetPath = dao->getPath();
360
361     LogDebug("Required path: " << req);
362     DPL::Optional<DPL::String> found =
363         GetFilePathInWidgetPackageInternal(widgetPath, req);
364
365     if (!found) {
366         LogError("Path not found within current locale in current widget");
367         return DPL::Optional<DPL::String>::Null;
368     }
369
370     found = widgetPath + *found + suffix;
371
372     return found;
373 }
374
375 DPL::Optional<DPL::String> getFilePathInWidgetPackage(
376     const WrtDB::TizenAppId &tzAppId,
377     const DPL::String& file)
378 {
379     return getFilePathInWidgetPackage(
380                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)),
381                file);
382 }
383
384 DPL::Optional<DPL::String> getFilePathInWidgetPackage(
385     WrtDB::WidgetDAOReadOnlyPtr dao,
386     const DPL::String& file)
387 {
388     return GetFilePathInWidgetPackageInternal(dao->getPath(), file);
389 }
390
391 DPL::OptionalString getStartFile(const WrtDB::TizenAppId & tzAppId)
392 {
393     return getStartFile(WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
394 }
395
396 DPL::OptionalString getStartFile(WrtDB::WidgetDAOReadOnlyPtr dao)
397 {
398     WidgetDAOReadOnly::LocalizedStartFileList locList =
399         dao->getLocalizedStartFileList();
400     WidgetDAOReadOnly::WidgetStartFileList list = dao->getStartFileList();
401     LanguageTags tagsList =
402         LanguageTagsProviderSingleton::Instance().getLanguageTags();
403
404     DPL::OptionalString defaultLoc = dao->getDefaultlocale();
405     if (!!defaultLoc) {
406         tagsList.push_back(*defaultLoc);
407     }
408
409     FOREACH(tag, tagsList)
410     {
411         FOREACH(sFile, locList)
412         {
413             if (*tag == sFile->widgetLocale) {
414                 FOREACH(it, list)
415                 {
416                     if (it->startFileId == sFile->startFileId) {
417                         return it->src;
418                     }
419                 }
420             }
421         }
422     }
423
424     return DPL::OptionalString::Null;
425 }
426
427 OptionalWidgetIcon getIcon(const WrtDB::TizenAppId & tzAppId)
428 {
429     return getIcon(WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
430 }
431
432 OptionalWidgetIcon getIcon(WrtDB::WidgetDAOReadOnlyPtr dao)
433 {
434     WidgetDAOReadOnly::WidgetLocalizedIconList locList =
435         dao->getLocalizedIconList();
436     WidgetDAOReadOnly::WidgetIconList list = dao->getIconList();
437     LanguageTags tagsList =
438         LanguageTagsProviderSingleton::Instance().getLanguageTags();
439
440     DPL::OptionalString defaultLoc = dao->getDefaultlocale();
441     if (!!defaultLoc) {
442         tagsList.push_back(*defaultLoc);
443     }
444
445     FOREACH(tag, tagsList)
446     {
447         FOREACH(icon, locList)
448         {
449             if (*tag == icon->widgetLocale) {
450                 FOREACH(it, list)
451                 {
452                     if (it->iconId == icon->iconId) {
453                         WidgetIcon ret;
454                         ret.src = it->iconSrc;
455                         ret.width = it->iconWidth;
456                         ret.height = it->iconHeight;
457                         return ret;
458                     }
459                 }
460             }
461         }
462     }
463
464     return OptionalWidgetIcon::Null;
465 }
466
467 WidgetIconList getValidIconsList(const WrtDB::TizenAppId &tzAppId)
468 {
469     return getValidIconsList(
470                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
471 }
472
473 WidgetIconList getValidIconsList(WrtDB::WidgetDAOReadOnlyPtr dao)
474 {
475     WidgetDAOReadOnly::WidgetIconList list = dao->getIconList();
476
477     WidgetIconList outlist;
478
479     FOREACH(it, list)
480     {
481         LogDebug(":" << it->iconSrc);
482         if (!!getFilePathInWidgetPackage(dao->getTizenAppId(),
483                                          it->iconSrc))
484         {
485             WidgetIcon ret;
486             ret.src = it->iconSrc;
487             ret.width = it->iconWidth;
488             ret.height = it->iconHeight;
489             outlist.push_back(ret);
490         }
491     }
492     return outlist;
493 }
494
495 OptionalWidgetStartFileInfo getStartFileInfo(
496     const WrtDB::TizenAppId &tzAppId)
497 {
498     return getStartFileInfo(
499                WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
500 }
501
502 OptionalWidgetStartFileInfo getStartFileInfo(WrtDB::WidgetDAOReadOnlyPtr dao)
503 {
504     WidgetStartFileInfo info;
505
506     WidgetDAOReadOnly::LocalizedStartFileList locList =
507         dao->getLocalizedStartFileList();
508     WidgetDAOReadOnly::WidgetStartFileList list = dao->getStartFileList();
509     const LanguageTags tagsList =
510         LanguageTagsProviderSingleton::Instance().getLanguageTags();
511
512     FOREACH(tag, tagsList)
513     {
514         FOREACH(sFile, locList)
515         {
516             if (*tag == sFile->widgetLocale) {
517                 FOREACH(it, list)
518                 {
519                     if (it->startFileId ==
520                         sFile->startFileId)
521                     {
522                         info.file = it->src;
523                         info.encoding = sFile->encoding;
524                         info.type = sFile->type;
525                         if (tag->empty()) {
526                             info.localizedPath = it->src;
527                         } else {
528                             info.localizedPath = L"locales/" + *tag + L"/";
529                             info.localizedPath += it->src;
530                         }
531                         return info;
532                     }
533                 }
534             }
535         }
536     }
537
538     return OptionalWidgetStartFileInfo::Null;
539 }
540
541 WidgetLocalizedInfo getLocalizedInfo(const WrtDB::TizenAppId & tzAppId)
542 {
543     return getLocalizedInfo(WidgetDAOReadOnlyPtr(new WidgetDAOReadOnly(tzAppId)));
544 }
545
546 WidgetLocalizedInfo getLocalizedInfo(WidgetDAOReadOnlyPtr dao)
547 {
548     LanguageTags languages =
549         LanguageTagsProviderSingleton::Instance().getLanguageTags();
550     DPL::OptionalString dl = dao->getDefaultlocale();
551     if (!!dl) {
552         languages.push_back(*dl);
553     }
554
555     WidgetLocalizedInfo result;
556     FOREACH(i, languages)
557     {
558         WidgetLocalizedInfo languageResult = dao->getLocalizedInfo(*i);
559
560 #define OVERWRITE_IF_NULL(FIELD) if (!result.FIELD) { \
561         result.FIELD = languageResult.FIELD; \
562 }
563
564         OVERWRITE_IF_NULL(name);
565         OVERWRITE_IF_NULL(shortName);
566         OVERWRITE_IF_NULL(description);
567         OVERWRITE_IF_NULL(license);
568         OVERWRITE_IF_NULL(licenseHref);
569
570 #undef OVERWRITE_IF_NULL
571     }
572
573     return result;
574 }
575 }