2f16427ab990f45c7ba64a6f56e9492008d6bc2b
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / visual-url.cpp
1 /*
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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 // CLASS HEADER
18 #include <dali-toolkit/internal/visuals/visual-url.h>
19
20 // EXTERNAL HEADERS
21 #include <dali/devel-api/common/hash.h>
22 #include <cstring> // for toupper()
23
24 namespace Dali
25 {
26 namespace Toolkit
27 {
28 namespace Internal
29 {
30 namespace
31 {
32 constexpr uint32_t URL_ELLIPSED_LENGTH = 20u;
33
34 static_assert(URL_ELLIPSED_LENGTH < URL_ELLIPSED_LENGTH + 3u); ///< Guard overflow cases. (for svace)
35
36 VisualUrl::ProtocolType ResolveLocation(const std::string& url)
37 {
38   const char*    urlCStr = url.c_str();
39   const uint32_t length  = url.size();
40   if((length > 7) && urlCStr[5] == ':' && urlCStr[6] == '/' && urlCStr[7] == '/')
41   {
42     // https:// or enbuf://
43     const char hOre = tolower(urlCStr[0]);
44     const char tOrn = tolower(urlCStr[1]);
45     const char tOrb = tolower(urlCStr[2]);
46     const char pOru = tolower(urlCStr[3]);
47     const char sOrf = tolower(urlCStr[4]);
48     if(('h' == hOre) &&
49        ('t' == tOrn) &&
50        ('t' == tOrb) &&
51        ('p' == pOru) &&
52        ('s' == sOrf))
53     {
54       return VisualUrl::REMOTE;
55     }
56     if(('e' == hOre) &&
57        ('n' == tOrn) &&
58        ('b' == tOrb) &&
59        ('u' == pOru) &&
60        ('f' == sOrf))
61     {
62       return VisualUrl::BUFFER;
63     }
64   }
65   else if((length > 6) && urlCStr[4] == ':' && urlCStr[5] == '/' && urlCStr[6] == '/')
66   {
67     // http:// or dali://
68     const char hOrd = tolower(urlCStr[0]);
69     const char tOra = tolower(urlCStr[1]);
70     const char tOrl = tolower(urlCStr[2]);
71     const char pOri = tolower(urlCStr[3]);
72     if(('h' == hOrd) &&
73        ('t' == tOra) &&
74        ('t' == tOrl) &&
75        ('p' == pOri))
76     {
77       return VisualUrl::REMOTE;
78     }
79     if(('d' == hOrd) &&
80        ('a' == tOra) &&
81        ('l' == tOrl) &&
82        ('i' == pOri))
83     {
84       return VisualUrl::TEXTURE;
85     }
86   }
87   else if((length > 5) && urlCStr[3] == ':' && urlCStr[4] == '/' && urlCStr[5] == '/')
88   {
89     // ftp:// or ssh://
90     const char fOrs = tolower(urlCStr[0]);
91     const char tOrs = tolower(urlCStr[1]);
92     const char pOrh = tolower(urlCStr[2]);
93     if(('f' == fOrs) &&
94        ('t' == tOrs) &&
95        ('p' == pOrh))
96     {
97       return VisualUrl::REMOTE;
98     }
99     if(('s' == fOrs) &&
100        ('s' == tOrs) &&
101        ('h' == pOrh))
102     {
103       return VisualUrl::REMOTE;
104     }
105   }
106   return VisualUrl::LOCAL;
107 }
108
109 VisualUrl::Type ResolveType(const std::string& url)
110 {
111   // if only one char in string, can only be regular image
112   const std::size_t count      = url.size();
113   VisualUrl::Type   returnType = VisualUrl::REGULAR_IMAGE;
114   if(count > 0)
115   {
116     // parsing from the end for better chance of early outs
117     enum
118     {
119       SUFFIX,
120       HASH,
121       HASH_DOT
122     } state                = SUFFIX;
123     char         SVG[4]    = {'g', 'v', 's', '.'};
124     char         GIF[4]    = {'f', 'i', 'g', '.'};
125     char         WEBP[5]   = {'p', 'b', 'e', 'w', '.'};
126     char         JSON[5]   = {'n', 'o', 's', 'j', '.'};
127     char         TVG[4]    = {'g', 'v', 't', '.'};
128     unsigned int svgScore  = 0;
129     unsigned int tvgScore  = 0;
130     unsigned int gifScore  = 0;
131     unsigned int webpScore = 0;
132     unsigned int jsonScore = 0;
133     int          index     = count;
134     while(--index >= 0)
135     {
136       const char        currentChar   = tolower(url[index]);
137       const std::size_t offsetFromEnd = count - index - 1u;
138       if((offsetFromEnd < sizeof(SVG)) && (currentChar == SVG[offsetFromEnd]))
139       {
140         // early out if SVG as can't be used in N patch for now
141         if(++svgScore == sizeof(SVG))
142         {
143           return VisualUrl::SVG;
144         }
145       }
146       if((offsetFromEnd < sizeof(TVG)) && (currentChar == TVG[offsetFromEnd]))
147       {
148         // early out if TVG as can't be used in N patch for now
149         if(++tvgScore == sizeof(TVG))
150         {
151           return VisualUrl::TVG;
152         }
153       }
154       if((offsetFromEnd < sizeof(GIF)) && (currentChar == GIF[offsetFromEnd]))
155       {
156         //find type, but need to be check used in N patch
157         if(++gifScore == sizeof(GIF))
158         {
159           returnType = VisualUrl::GIF;
160         }
161       }
162       if((offsetFromEnd < sizeof(WEBP)) && (currentChar == WEBP[offsetFromEnd]))
163       {
164         if(++webpScore == sizeof(WEBP))
165         {
166           //find type, but need to be check used in N patch
167           returnType = VisualUrl::WEBP;
168         }
169       }
170       if((offsetFromEnd < sizeof(JSON)) && (currentChar == JSON[offsetFromEnd]))
171       {
172         // early out if JSON as can't be used in N patch for now
173         if(++jsonScore == sizeof(JSON))
174         {
175           return VisualUrl::JSON;
176         }
177       }
178       switch(state)
179       {
180         case SUFFIX:
181         {
182           if('.' == currentChar)
183           {
184             state = HASH;
185           }
186           break;
187         }
188         case HASH:
189         {
190           if(('#' == currentChar) || ('9' == currentChar))
191           {
192             state = HASH_DOT;
193           }
194           else
195           {
196             // early out, not a valid N/9-patch URL
197             return returnType;
198           }
199           break;
200         }
201         case HASH_DOT:
202         {
203           if('.' == currentChar)
204           {
205             return VisualUrl::N_PATCH;
206           }
207           else
208           {
209             // early out, not a valid N/9-patch URL
210             return returnType;
211           }
212           break;
213         }
214       }
215     }
216   }
217   // if we got here it is a regular image
218   return returnType;
219 }
220
221 } // namespace
222
223 VisualUrl::VisualUrl()
224 : mUrl(),
225   mType(VisualUrl::REGULAR_IMAGE),
226   mLocation(VisualUrl::LOCAL),
227   mUrlHash(0ull)
228 {
229 }
230
231 VisualUrl::VisualUrl(const std::string& url)
232 : mUrl(url),
233   mType(VisualUrl::REGULAR_IMAGE),
234   mLocation(VisualUrl::LOCAL),
235   mUrlHash(0ull)
236 {
237   if(!url.empty())
238   {
239     mLocation = ResolveLocation(url);
240     if(VisualUrl::TEXTURE != mLocation)
241     {
242       // TEXTURE location url doesn't need type resolving, REGULAR_IMAGE is fine
243       mType = ResolveType(url);
244     }
245   }
246 }
247
248 VisualUrl::VisualUrl(const VisualUrl& url)
249 : mUrl(url.mUrl),
250   mType(url.mType),
251   mLocation(url.mLocation),
252   mUrlHash(url.mUrlHash)
253 {
254 }
255
256 VisualUrl::VisualUrl(VisualUrl&& url) noexcept
257 : mUrl(std::move(url.mUrl)),
258   mType(std::move(url.mType)),
259   mLocation(std::move(url.mLocation)),
260   mUrlHash(std::move(url.mUrlHash))
261 {
262   url.mUrlHash = 0ull;
263 }
264
265 VisualUrl::~VisualUrl()
266 {
267 }
268
269 VisualUrl& VisualUrl::operator=(const VisualUrl& url)
270 {
271   if(&url != this)
272   {
273     mUrl      = url.mUrl;
274     mType     = url.mType;
275     mLocation = url.mLocation;
276     mUrlHash  = url.mUrlHash;
277   }
278   return *this;
279 }
280
281 VisualUrl& VisualUrl::operator=(VisualUrl&& url) noexcept
282 {
283   if(&url != this)
284   {
285     mUrl      = std::move(url.mUrl);
286     mType     = std::move(url.mType);
287     mLocation = std::move(url.mLocation);
288     mUrlHash  = std::move(url.mUrlHash);
289
290     url.mUrlHash = 0ull;
291   }
292   return *this;
293 }
294
295 const std::string& VisualUrl::GetUrl() const
296 {
297   return mUrl;
298 }
299
300 std::string VisualUrl::GetEllipsedUrl() const
301 {
302   if(mUrl.size() > URL_ELLIPSED_LENGTH + 3)
303   {
304     std::string ellipsedUrl = "...";
305     ellipsedUrl += mUrl.substr(mUrl.size() - URL_ELLIPSED_LENGTH);
306     return ellipsedUrl;
307   }
308   return mUrl;
309 }
310
311 std::uint64_t VisualUrl::GetUrlHash() const
312 {
313   return DALI_UNLIKELY(mUrlHash == 0) ? (mUrlHash = Dali::CalculateHash(mUrl)) : mUrlHash;
314 }
315
316 VisualUrl::Type VisualUrl::GetType() const
317 {
318   return mType;
319 }
320
321 VisualUrl::ProtocolType VisualUrl::GetProtocolType() const
322 {
323   return mLocation;
324 }
325
326 bool VisualUrl::IsValid() const
327 {
328   return mUrl.size() > 0u;
329 }
330
331 bool VisualUrl::IsLocalResource() const
332 {
333   return mLocation == VisualUrl::LOCAL;
334 }
335
336 bool VisualUrl::IsBufferResource() const
337 {
338   return mLocation == VisualUrl::BUFFER;
339 }
340
341 std::string VisualUrl::GetLocation() const
342 {
343   return GetLocation(mUrl);
344 }
345
346 std::string VisualUrl::GetLocationWithoutExtension() const
347 {
348   return GetLocationWithoutExtension(mUrl);
349 }
350
351 std::string VisualUrl::CreateTextureUrl(const std::string& location)
352 {
353   return "dali://" + location;
354 }
355
356 std::string VisualUrl::CreateBufferUrl(const std::string& location, const std::string_view& extension)
357 {
358   return "enbuf://" + location + std::string(extension);
359 }
360
361 VisualUrl::ProtocolType VisualUrl::GetProtocolType(const std::string& url)
362 {
363   return ResolveLocation(url);
364 }
365
366 std::string VisualUrl::GetLocation(const std::string& url)
367 {
368   const auto location = url.find("://");
369   if(std::string::npos != location)
370   {
371     return url.substr(location + 3u); // 3 characters forwards from the start of ://
372   }
373   return url;
374 }
375
376 std::string VisualUrl::GetLocationWithoutExtension(const std::string& url)
377 {
378   const auto location = url.find("://");
379   if(std::string::npos != location)
380   {
381     const auto extension      = url.find_last_of("."); // Find last position of '.' keyword.
382     const auto locationLength = extension != std::string::npos ? extension - (location + 3u) : std::string::npos;
383     return url.substr(location + 3u, locationLength); // 3 characters forwards from the start of ://, and end of last '.' keyword.
384   }
385   return url;
386 }
387
388 } // namespace Internal
389
390 } // namespace Toolkit
391
392 } // namespace Dali