1 /** @file uri.hh Universal Resource Identifier.
2 * @ingroup base
3 *
4 * @author Copyright © 2010-2013 Daniel Swanson <danij@dengine.net>
5 * @author Copyright © 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