1 /** @file uri.hh Universal Resource Identifier.
2  * @ingroup base
3  *
4  * @author Copyright &copy; 2010-2013 Daniel Swanson <danij@dengine.net>
5  * @author Copyright &copy; 2010-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
6  *
7  * @par License
8  * GPL: http://www.gnu.org/licenses/gpl.html
9  *
10  * <small>This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version. This program is distributed in the hope that it
14  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details. You should have received a copy of the GNU
17  * General Public License along with this program; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA</small>
20  */
21 
22 #ifndef LIBDOOMSDAY_URI_H
23 #define LIBDOOMSDAY_URI_H
24 
25 #ifdef __cplusplus
26 
27 #include "libdoomsday.h"
28 #include "resource/resourceclass.h"
29 
30 /// Schemes must be at least this many characters.
31 #define DENG2_URI_MIN_SCHEME_LENGTH     2
32 
33 #include <de/Log>
34 #include <de/Error>
35 #include <de/NativePath>
36 #include <de/String>
37 
38 struct reader_s;
39 struct writer_s;
40 struct ddstring_s; // libdeng Str
41 
42 namespace de {
43 
44 /**
45  * Assists working with URIs and maps them to engine-managed resources.
46  * @ingroup base
47  *
48  * Uri is derived from Path. It augments Path with schemes and path symbolics.
49  *
50  * Universal resource identifiers (URIs) are a way to identify specific
51  * entities in a hierarchy.
52  */
53 class LIBDOOMSDAY_PUBLIC Uri : public ISerializable
54 {
55 public:
56     /// Base class for resolve-related errors. @ingroup errors
57     DENG2_ERROR(ResolveError);
58 
59     /// An unknown symbol was encountered in the embedded expression. @ingroup errors
60     DENG2_SUB_ERROR(ResolveError, UnknownSymbolError);
61 
62     /// An unresolveable symbol was encountered in the embedded expression. @ingroup errors
63     DENG2_SUB_ERROR(ResolveError, ResolveSymbolError);
64 
65     /**
66      * Flags determining the composition of textual representation:
67      */
68     enum ComposeAsTextFlag
69     {
70         OmitScheme      = 0x1, ///< Exclude the scheme.
71         OmitPath        = 0x2, ///< Exclude the path.
72         DecodePath      = 0x4, ///< Decode percent-endcoded characters in the path.
73 
74         DefaultComposeAsTextFlags = 0
75     };
76     Q_DECLARE_FLAGS(ComposeAsTextFlags, ComposeAsTextFlag)
77 
78     typedef String (*ResolverFunc)(String const &symbol);
79 
80 public:
81     /**
82      * Construct an empty Uri instance.
83      */
84     Uri();
85 
86     Uri(String const &percentEncoded);
87 
88     /**
89      * Construct a Uri instance from a text string.
90      *
91      * @param percentEncoded  String to be parsed. Assumed to be in
92      *                        percent-encoded representation.
93      *
94      * @param defaultResClass Default scheme. Determines the scheme for the Uri
95      *                        if one is not specified in @a percentEncoded.
96      *                        @c RC_IMPLICIT: resource locator guesses an
97      *                        appropriate scheme for this type of file.
98      *
99      * @param sep             Character used to separate path segments
100      *                        in @a path.
101      */
102     Uri(String const &percentEncoded, resourceclassid_t defaultResClass, QChar sep = '/');
103 
104     /**
105      * Construct a Uri from a textual scheme and a path.
106      *
107      * @param scheme  Scheme for the Uri.
108      * @param path    Path for the Uri.
109      */
110     Uri(String const &scheme, Path const &path);
111 
112     /**
113      * Construct a Uri instance from a path. Note that Path instances can
114      * never contain a scheme as a prefix, so @a resClass is mandatory.
115      *
116      * @param resClass  Scheme for the URI. @c RC_IMPLICIT: resource locator
117      *                  guesses an appropriate scheme for this.
118      *
119      * @param path      Path of the URI.
120      */
121     Uri(resourceclassid_t resClass, Path const &path);
122 
123     /**
124      * Construct a Uri instance from a path without a scheme.
125      *
126      * @param path      Path of the URI.
127      */
128     Uri(Path const &path);
129 
130     /**
131      * Construct a Uri instance from a UTF-8 C-style text string, using RC_IMPLICIT
132      * as the default resource class.
133      *
134      * @param nullTerminatedCStr  String to be parsed. Assumed to be in
135      *                            percent-encoded representation.
136      */
137     Uri(char const *nullTerminatedCStr);
138 
139     /**
140      * Construct a Uri instance by duplicating @a other.
141      */
142     Uri(Uri const &other);
143 
144     inline Uri &operator = (Uri other) {
145         d.swap(other.d);
146         return *this;
147     }
148 
149     /**
150      * Swaps this Uri with @a other.
151      * @param other  Uri.
152      */
swap(Uri & other)153     inline void swap(Uri &other) {
154         d.swap(other.d);
155     }
156 
157     bool operator == (Uri const &other) const;
158 
159     bool operator != (Uri const &other) const {
160         return !(*this == other);
161     }
162 
163     /**
164      * Constructs a Uri instance from a NativePath that refers to a file in
165      * the native file system. All path directives such as '~' are
166      * expanded. The resultant Uri will have an empty (zero-length) scheme
167      * (because file paths do not include one).
168      *
169      * @param path  Native path to a file in the native file system.
170      * @param defaultResourceClass  Default resource class.
171      */
172     static Uri fromNativePath(NativePath const &path,
173                               resourceclassid_t defaultResourceClass = RC_NULL);
174 
175     /**
176      * Constructs a Uri instance from a NativePath that refers to a native
177      * directory. All path directives such as '~' are expanded. The
178      * resultant Uri will have an empty (zero-length) scheme (because file
179      * paths do not include one).
180      *
181      * @param nativeDirPath  Native path to a directory in the native
182      *                       file system.
183      * @param defaultResourceClass  Default resource class.
184      */
185     static Uri fromNativeDirPath(NativePath const &nativeDirPath,
186                                  resourceclassid_t defaultResourceClass = RC_NULL);
187 
188     /**
189      * Construct a Uri instance from a user supplied, variable-length list of UTF-8
190      * C-style text string arguments.
191      *
192      * @param argv         The arguments to be interpreted. All of which are assumed to
193      *                     be in a @em non-percent-encoded representation.
194      *
195      *                     Supported forms (where <> denote keyword component names):
196      *                      - [0: "<scheme>:<path>"]
197      *                      - [0: "<scheme>"]              (if @a knownScheme set)
198      *                      - [0: "<path>"]
199      *                      - [0: "<scheme>", 1: "<path>"]
200      *
201      * @param argc         The number of elements in @a argv.
202      *
203      * @param knownScheme  Callback function used to identify known scheme names when
204      *                     attempting to resolve ambiguous cases (only the one argument
205      *                     is provided.
206      */
207     static Uri fromUserInput(char **argv, int argc, bool (*knownScheme) (String name) = 0);
208 
209     /**
210      * Convert this URI to a text string.
211      *
212      * @see asText()
213      */
String()214     operator String () const {
215         return asText();
216     }
217 
218     /**
219      * Determines if the URI's path is empty.
220      */
221     bool isEmpty() const;
222 
223     /**
224      * Clear the URI returning it to an empty state.
225      */
226     Uri &clear();
227 
228     /**
229      * Attempt to resolve this URI. Substitutes known symbolics in the possibly
230      * templated path. Resulting path is a well-formed, filesys compatible path
231      * (perhaps base-relative).
232      *
233      * @return  The resolved path.
234      */
235     String resolved() const;
236 
237     String const &resolvedRef() const;
238 
239     /**
240      * @return Scheme of the URI.
241      */
242     String const &scheme() const;
243 
244     /**
245      * @return Path of the URI.
246      */
247     Path const &path() const;
248 
249     /**
250      * @return Scheme of the URI as plain text (UTF-8 encoding).
251      */
252     char const *schemeCStr() const;
253 
254     /**
255      * @return  Path of the URI as plain text (UTF-8 encoding).
256      */
257     char const *pathCStr() const;
258 
259     /**
260      * @return  Scheme of the URI as plain text (UTF-8 encoding).
261      */
262     struct ddstring_s const *schemeStr() const;
263 
264     /**
265      * @return  Path of the URI as plain text (UTF-8 encoding).
266      */
267     struct ddstring_s const *pathStr() const;
268 
269     /**
270      * Change the scheme of the URI to @a newScheme.
271      */
272     Uri &setScheme(String newScheme);
273 
274     /**
275      * Change the path of the URI to @a newPath.
276      *
277      * @param newPath  New path for the URI.
278      */
279     Uri &setPath(Path const &newPath);
280 
281     /**
282      * Change the path of the URI to @a newPath.
283      *
284      * @param newPath  New path for the URI.
285      * @param sep      Character used to separate path segments in @a path.
286      */
287     Uri &setPath(String newPath, QChar sep = '/');
288 
289     Uri &setPath(char const *newPathUtf8, char sep = '/');
290 
291     /**
292      * Update this URI by parsing new values from the specified arguments.
293      *
294      * @param newUri  URI to be parsed. Assumed to be in percent-encoded representation.
295      *
296      * @param defaultResourceClass  If no scheme is defined in @a newUri and
297      *      this is not @c RC_NULL, ask the resource locator whether it knows
298      *      of an appropriate default scheme for this class of resource.
299      *
300      * @param sep  Character used to separate path segments in @a path.
301      */
302     Uri &setUri(String newUri, resourceclassid_t defaultResourceClass = RC_IMPLICIT, QChar sep = '/');
303 
304     /**
305      * Compose from this URI a plain-text representation. Any symbolic identifiers
306      * will be left unchanged in the resultant string (not resolved).
307      *
308      * @param compositionFlags  Flags determining how the textual representation
309      *                          is composited.
310      * @param sep  Character to use to replace path segment separators.
311      *
312      * @return  Plain-text String representation.
313      */
314     String compose(ComposeAsTextFlags compositionFlags = DefaultComposeAsTextFlags,
315                    QChar sep = '/') const;
316 
317     /**
318      * Transform the URI into a human-friendly representation. Percent-encoded
319      * symbols are decoded.
320      *
321      * @return  Human-friendly representation of the URI.
322      *
323      * Same as @code compose(DefaultComposeAsTextFlags | DecodePath); @endcode
324      */
325     String asText() const;
326 
327     // Implements ISerializable.
328     void operator >> (Writer &to) const override;
329     void operator << (Reader &from) override;
330 
331     // Legacy Reader/Writer.
332     void readUri(reader_s *reader, de::String defaultScheme = "");
333     void writeUri(writer_s *writer, int omitComponents = 0) const;
334 
335 public:
336     /**
337      * Sets the function that is used for resolving symbols in Uris.
338      *
339      * @param resolver  Resolver callback function.
340      */
341     static void setResolverFunc(ResolverFunc resolver);
342 
343 private:
344     DENG2_PRIVATE(d)
345 };
346 
Q_DECLARE_OPERATORS_FOR_FLAGS(Uri::ComposeAsTextFlags)347 Q_DECLARE_OPERATORS_FOR_FLAGS(Uri::ComposeAsTextFlags)
348 
349 inline Uri makeUri(String const &percentEncoded, QChar sep = '/')
350 {
351     return Uri(percentEncoded, RC_NULL, sep);
352 }
353 
354 } // namespace de
355 
356 namespace std {
357     // std::swap specialization for de::Uri
358     template <>
359     inline void swap<de::Uri>(de::Uri &a, de::Uri &b) {
360         a.swap(b);
361     }
362 }
363 
364 #endif // __cplusplus
365 
366 #endif // LIBDOOMSDAY_URI_H
367