1 #pragma once
2 
3 #include "types.hpp"
4 #include "config_mergeable.hpp"
5 #include "config_origin.hpp"
6 #include "config_object.hpp"
7 #include "config_resolve_options.hpp"
8 #include "config_value.hpp"
9 #include "config_list.hpp"
10 #include "config_exception.hpp"
11 #include "path.hpp"
12 #include <vector>
13 #include <string>
14 #include <set>
15 #include "export.h"
16 #include <leatherman/locale/locale.hpp>
17 
18 namespace hocon {
19 
20     enum class time_unit { NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS };
21 
22     /**
23      * An immutable map from config paths to config values. Paths are dot-separated
24      * expressions such as <code>foo.bar.baz</code>. Values are as in JSON
25      * (booleans, strings, numbers, lists, or objects), represented by
26      * {@link config_value} instances. Values accessed through the
27      * <code>config</code> interface are never null.
28      *
29      * <p>
30      * {@code config} is an immutable object and thus safe to use from multiple
31      * threads. There's never a need for "defensive copies."
32      *
33      * <p>
34      * Fundamental operations on a {@code config} include getting configuration
35      * values, <em>resolving</em> substitutions with {@link config#resolve()}, and
36      * merging configs using {@link config#with_fallback(config_mergeable)}.
37      *
38      * <p>
39      * All operations return a new immutable {@code config} rather than modifying
40      * the original instance.
41      *
42      * <p>
43      * <strong>Examples</strong>
44      *
45      * <p>
46      * You can find an example app and library <a
47      * href="https://github.com/typesafehub/config/tree/master/examples">on
48      * GitHub</a>. Also be sure to read the <a
49      * href="package-summary.html#package_description">package overview</a> which
50      * describes the big picture as shown in those examples.
51      *
52      * <p>
53      * <strong>Paths, keys, and config vs. config_object</strong>
54      *
55      * <p>
56      * <code>config</code> is a view onto a tree of {@link config_object}; the
57      * corresponding object tree can be found through {@link config#root()}.
58      * <code>config_object</code> is a map from config <em>keys</em>, rather than
59      * paths, to config values. Think of <code>config_object</code> as a JSON object
60      * and <code>config</code> as a configuration API.
61      *
62      * <p>
63      * The API tries to consistently use the terms "key" and "path." A key is a key
64      * in a JSON object; it's just a string that's the key in a map. A "path" is a
65      * parseable expression with a syntax and it refers to a series of keys. Path
66      * expressions are described in the <a
67      * href="https://github.com/typesafehub/config/blob/master/HOCON.md">spec for
68      * Human-Optimized Config Object Notation</a>. In brief, a path is
69      * period-separated so "a.b.c" looks for key c in object b in object a in the
70      * root object. Sometimes double quotes are needed around special characters in
71      * path expressions.
72      *
73      * <p>
74      * The API for a {@code config} is in terms of path expressions, while the API
75      * for a {@code config_object} is in terms of keys. Conceptually, {@code config}
76      * is a one-level map from <em>paths</em> to values, while a
77      * {@code config_object} is a tree of nested maps from <em>keys</em> to values.
78      *
79      * <p>
80      * Use {@link config_util#join_path} and {@link config_util#split_path} to convert
81      * between path expressions and individual path elements (keys).
82      *
83      * <p>
84      * Another difference between {@code config} and {@code config_object} is that
85      * conceptually, {@code config_value}s with a {@link config_value#value_type()
86      * value_type()} of {@link config_value::type#NULL NULL} exist in a
87      * {@code config_object}, while a {@code config} treats null values as if they
88      * were missing. (With the exception of two methods: {@link config#has_path_or_null}
89      * and {@link config#get_is_null} let you detect <code>null</code> values.)
90      *
91      * <p>
92      * <strong>Getting configuration values</strong>
93      *
94      * <p>
95      * The "getters" on a {@code config} all work in the same way. They never return
96      * null, nor do they return a {@code config_value} with
97      * {@link config_value#value_type() value_type()} of {@link config_value::type#NULL
98      * NULL}. Instead, they throw {@link config_exception} if the value is
99      * completely absent or set to null. If the value is set to null, a subtype of
100      * {@code config_exception.missing} called {@link config_exception.null} will be
101      * thrown. {@link config_excpetion.wrong_type} will be thrown anytime you ask for
102      * a type and the value has an incompatible type. Reasonable type conversions
103      * are performed for you though.
104      *
105      * <p>
106      * <strong>Iteration</strong>
107      *
108      * <p>
109      * If you want to iterate over the contents of a {@code config}, you can get its
110      * {@code config_object} with {@link #root()}, and then iterate over the
111      * {@code config_object} (which implements <code>java.util.Map</code>). Or, you
112      * can use {@link #entry_set()} which recurses the object tree for you and builds
113      * up a <code>set</code> of all path-value pairs where the value is not null.
114      *
115      * <p>
116      * <strong>Resolving substitutions</strong>
117      *
118      * <p>
119      * <em>Substitutions</em> are the <code>${foo.bar}</code> syntax in config
120      * files, described in the <a href=
121      * "https://github.com/typesafehub/config/blob/master/HOCON.md#substitutions"
122      * >specification</a>. Resolving substitutions replaces these references with real
123      * values.
124      *
125      * <p>
126      * Before using a {@code config} it's necessary to call {@link config#resolve()}
127      * to handle substitutions (though {@link config_factory#load()} and similar
128      * methods will do the resolve for you already).
129      *
130      * <p>
131      * <strong>Merging</strong>
132      *
133      * <p>
134      * The full <code>config</code> for your application can be constructed using
135      * the associative operation {@link config#with_fallback(config_mergeable)}. If
136      * you use {@link config_factory#load()} (recommended), it merges system
137      * properties over the top of <code>application.conf</code> over the top of
138      * <code>reference.conf</code>, using <code>with_fallback</code>. You can add in
139      * additional sources of configuration in the same way (usually, custom layers
140      * should go either just above or just below <code>application.conf</code>,
141      * keeping <code>reference.conf</code> at the bottom and system properties at
142      * the top).
143      *
144      * <p>
145      * <strong>Serialization</strong>
146      *
147      * <p>
148      * Convert a <code>config</code> to a JSON or HOCON string by calling
149      * {@link config_object#render()} on the root object,
150      * <code>my_config.root().render()</code>. There's also a variant
151      * {@link config_object#render(config_render_options)} which allows you to control
152      * the format of the rendered string. (See {@link config_render_options}.) Note
153      * that <code>config</code> does not remember the formatting of the original
154      * file, so if you load, modify, and re-save a config file, it will be
155      * substantially reformatted.
156      *
157      * <p>
158      * As an alternative to {@link config_object#render()}, the
159      * <code>to_string()</code> method produces a debug-output-oriented
160      * representation (which is not valid JSON).
161      *
162      * <p>
163      * <strong>This is an interface but don't implement it yourself</strong>
164      *
165      * <p>
166      * <em>Do not implement {@code config}</em>; it should only be implemented by
167      * the config library. Arbitrary implementations will not work because the
168      * library internals assume a specific concrete implementation. Also, this
169      * interface is likely to grow new methods over time, so third-party
170      * implementations will break.
171      */
172     class LIBCPP_HOCON_EXPORT config : public config_mergeable, public std::enable_shared_from_this<config> {
173         friend class config_object;
174         friend class config_value;
175         friend class config_parseable;
176         friend class parseable;
177 
178     public:
179         /**
180          * Parses a file with a flexible extension. If the <code>fileBasename</code>
181          * already ends in a known extension, this method parses it according to
182          * that extension (the file's syntax must match its extension). If the
183          * <code>fileBasename</code> does not end in an extension, it parses files
184          * with all known extensions and merges whatever is found.
185          *
186          * <p>
187          * In the current implementation, the extension ".conf" forces
188          * {@link ConfigSyntax#CONF}, ".json" forces {@link ConfigSyntax#JSON}.
189          * When merging files, ".conf" falls back to ".json".
190          *
191          * <p>
192          * Future versions of the implementation may add additional syntaxes or
193          * additional extensions. However, the ordering (fallback priority) of the
194          * three current extensions will remain the same.
195          *
196          * <p>
197          * If <code>options</code> forces a specific syntax, this method only parses
198          * files with an extension matching that syntax.
199          *
200          * <p>
201          * If {@link ConfigParseOptions#getAllowMissing options.getAllowMissing()}
202          * is true, then no files have to exist; if false, then at least one file
203          * has to exist.
204          *
205          * @param fileBasename
206          *            a filename with or without extension
207          * @param options
208          *            parse options
209          * @return the parsed configuration
210          */
211         static shared_config parse_file_any_syntax(std::string file_basename, config_parse_options options);
212 
213         /**
214          * Like {@link #parseFileAnySyntax(File,ConfigParseOptions)} but always uses
215          * default parse options.
216          *
217          * @param fileBasename
218          *            a filename with or without extension
219          * @return the parsed configuration
220          */
221         static shared_config parse_file_any_syntax(std::string file_basename);
222 
223         /**
224          * Parses a string (which should be valid HOCON or JSON by default, or
225          * the syntax specified in the options otherwise).
226          *
227          * @param s string to parse
228          * @param options parse options
229          * @return the parsed configuration
230          */
231         static shared_config parse_string(std::string s, config_parse_options options);
232 
233         /**
234          * Parses a string (which should be valid HOCON or JSON).
235          *
236          * @param s string to parse
237          * @return the parsed configuration
238          */
239         static shared_config parse_string(std::string s);
240 
241         /**
242          * Gets the {@code Config} as a tree of {@link ConfigObject}. This is a
243          * constant-time operation (it is not proportional to the number of values
244          * in the {@code Config}).
245          *
246          * @return the root object in the configuration
247          */
248         virtual shared_object root() const;
249 
250         /**
251          * Gets the origin of the {@code Config}, which may be a file, or a file
252          * with a line number, or just a descriptive phrase.
253          *
254          * @return the origin of the {@code Config} for use in error messages
255          */
256         virtual shared_origin origin() const;
257 
258         std::shared_ptr<const config_mergeable> with_fallback(std::shared_ptr<const config_mergeable> other) const override;
259 
260         shared_value to_fallback_value() const override;
261 
262         /**
263          * Returns a replacement config with all substitutions (the
264          * <code>${foo.bar}</code> syntax, see <a
265          * href="https://github.com/typesafehub/config/blob/master/HOCON.md">the
266          * spec</a>) resolved. Substitutions are looked up using this
267          * <code>config</code> as the root object, that is, a substitution
268          * <code>${foo.bar}</code> will be replaced with the result of
269          * <code>get_value("foo.bar")</code>.
270          *
271          * <p>
272          * This method uses {@link config_resolve_options()}, there is
273          * another variant {@link config#resolve(config_resolve_options)} which lets
274          * you specify non-default options.
275          *
276          * <p>
277          * A given {@link config} must be resolved before using it to retrieve
278          * config values, but ideally should be resolved one time for your entire
279          * stack of fallbacks (see {@link config#with_fallback}). Otherwise, some
280          * substitutions that could have resolved with all fallbacks available may
281          * not resolve, which will be potentially confusing for your application's
282          * users.
283          *
284          * <p>
285          * <code>resolve()</code> should be invoked on root config objects, rather
286          * than on a subtree (a subtree is the result of something like
287          * <code>config.get_config("foo")</code>). The problem with
288          * <code>resolve()</code> on a subtree is that substitutions are relative to
289          * the root of the config and the subtree will have no way to get values
290          * from the root. For example, if you did
291          * <code>config.get_config("foo").resolve()</code> on the below config file,
292          * it would not work:
293          *
294          * <pre>
295          *   common-value = 10
296          *   foo {
297          *      whatever = ${common-value}
298          *   }
299          * </pre>
300          *
301          * <p>
302          * Many methods on {@link config_factory} such as
303          * {@link config_factory#load()} automatically resolve the loaded
304          * <code>config</code> on the loaded stack of config files.
305          *
306          * <p>
307          * Resolving an already-resolved config is a harmless no-op, but again, it
308          * is best to resolve an entire stack of fallbacks (such as all your config
309          * files combined) rather than resolving each one individually.
310          *
311          * @return an immutable object with substitutions resolved
312          */
313         virtual shared_config resolve() const;
314 
315         /**
316          * Like {@link config#resolve()} but allows you to specify non-default
317          * options.
318          *
319          * @param options
320          *            resolve options
321          * @return the resolved <code>config</code> (may be only partially resolved if options are
322          *          set to allow unresolved)
323          */
324         virtual shared_config resolve(config_resolve_options options) const;
325 
326         /**
327          * Checks whether the config is completely resolved. After a successful call
328          * to {@link config#resolve()} it will be completely resolved, but after
329          * calling {@link config#resolve(config_resolve_options)} with
330          * <code>allow_unresolved</code> set in the options, it may or may not be
331          * completely resolved. A newly-loaded config may or may not be completely
332          * resolved depending on whether there were substitutions present in the
333          * file.
334          *
335          * @return true if there are no unresolved substitutions remaining in this
336          *         configuration.
337          */
338         virtual bool is_resolved() const;
339 
340         /**
341          * Like {@link config#resolve()} except that substitution values are looked
342          * up in the given source, rather than in this instance. This is a
343          * special-purpose method which doesn't make sense to use in most cases;
344          * it's only needed if you're constructing some sort of app-specific custom
345          * approach to configuration. The more usual approach if you have a source
346          * of substitution values would be to merge that source into your config
347          * stack using {@link config#withFallback} and then resolve.
348          * <p>
349          * Note that this method does NOT look in this instance for substitution
350          * values. If you want to do that, you could either merge this instance into
351          * your value source using {@link config#with_fallback}, or you could resolve
352          * multiple times with multiple sources (using
353          * {@link config_resolve_options#setAllowUnresolved(boolean)} so the partial
354          * resolves don't fail).
355          *
356          * @param source
357          *            configuration to pull values from
358          * @return an immutable object with substitutions resolved
359          */
360         virtual shared_config resolve_with(shared_config source) const;
361 
362         /**
363          * Like {@link config#resolve_with(config)} but allows you to specify
364          * non-default options.
365          *
366          * @param source
367          *            source configuration to pull values from
368          * @param options
369          *            resolve options
370          * @return the resolved <code>config</code> (may be only partially resolved
371          *         if options are set to allow unresolved)
372          */
373         virtual shared_config resolve_with(shared_config source, config_resolve_options options) const;
374 
375         /**
376          * Validates this config against a reference config, throwing an exception
377          * if it is invalid. The purpose of this method is to "fail early" with a
378          * comprehensive list of problems; in general, anything this method can find
379          * would be detected later when trying to use the config, but it's often
380          * more user-friendly to fail right away when loading the config.
381          *
382          * <p>
383          * Using this method is always optional, since you can "fail late" instead.
384          *
385          * <p>
386          * You must restrict validation to paths you "own" (those whose meaning are
387          * defined by your code module). If you validate globally, you may trigger
388          * errors about paths that happen to be in the config but have nothing to do
389          * with your module. It's best to allow the modules owning those paths to
390          * validate them. Also, if every module validates only its own stuff, there
391          * isn't as much redundant work being done.
392          *
393          * <p>
394          * If no paths are specified in <code>check_valid()</code>'s parameter list,
395          * validation is for the entire config.
396          *
397          * <p>
398          * If you specify paths that are not in the reference config, those paths
399          * are ignored. (There's nothing to validate.)
400          *
401          * <p>
402          * Here's what validation involves:
403          *
404          * <ul>
405          * <li>All paths found in the reference config must be present in this
406          * config or an exception will be thrown.
407          * <li>
408          * Some changes in type from the reference config to this config will cause
409          * an exception to be thrown. Not all potential type problems are detected,
410          * in particular it's assumed that strings are compatible with everything
411          * except objects and lists. This is because string types are often "really"
412          * some other type (system properties always start out as strings, or a
413          * string like "5ms" could be used with {@link #get_milliseconds}). Also,
414          * it's allowed to set any type to null or override null with any type.
415          * <li>
416          * Any unresolved substitutions in this config will cause a validation
417          * failure; both the reference config and this config should be resolved
418          * before validation. If the reference config is unresolved, it's a bug in
419          * the caller of this method.
420          * </ul>
421          *
422          * <p>
423          * If you want to allow a certain setting to have a flexible type (or
424          * otherwise want validation to be looser for some settings), you could
425          * either remove the problematic setting from the reference config provided
426          * to this method, or you could intercept the validation exception and
427          * screen out certain problems. Of course, this will only work if all other
428          * callers of this method are careful to restrict validation to their own
429          * paths, as they should be.
430          *
431          * <p>
432          * If validation fails, the thrown exception contains a list of all problems
433          * found. The exception will have all the problem concatenated into one huge string.
434          *
435          * <p>
436          * Again, <code>check_valid()</code> can't guess every domain-specific way a
437          * setting can be invalid, so some problems may arise later when attempting
438          * to use the config. <code>check_valid()</code> is limited to reporting
439          * generic, but common, problems such as missing settings and blatant type
440          * incompatibilities.
441          *
442          * @param reference
443          *            a reference configuration
444          * @param restrictToPaths
445          *            only validate values underneath these paths that your code
446          *            module owns and understands
447          */
448         virtual void check_valid(shared_config reference, std::vector<std::string> restrict_to_paths) const;
449 
450         /**
451          * Checks whether a value is present and non-null at the given path. This
452          * differs in two ways from {@code Map.containsKey()} as implemented by
453          * {@link config_object}: it looks for a path expression, not a key; and it
454          * returns false for null values, while {@code contains_key()} returns true
455          * indicating that the object contains a null value for the key.
456          *
457          * <p>
458          * If a path exists according to {@link #has_path(string)}, then
459          * {@link #get_value(string)} will never throw an exception. However, the
460          * typed getters will still throw if the value is not convertible to the
461          * requested type.
462          *
463          * <p>
464          * Note that path expressions have a syntax and sometimes require quoting
465          * (see {@link config_util#join_path} and {@link config_util#split_path}).
466          *
467          * @param path
468          *            the path expression
469          * @return true if a non-null value is present at the path
470          */
471         virtual bool has_path(std::string const& path) const;
472 
473         /**
474          * Checks whether a value is present at the given path, even
475          * if the value is null. Most of the getters on
476          * <code>config</code> will throw if you try to get a null
477          * value, so if you plan to call {@link #get_value(string)},
478          * {@link #get_int(string)}, or another getter you may want to
479          * use plain {@link #has_path(string)} rather than this method.
480          *
481          * <p>
482          * To handle all three cases (unset, null, and a non-null value)
483          * the code might look like:
484          * <pre><code>
485          * if (config.has_path_or_null(path)) {
486          *     if (config.get_is_null(path)) {
487          *        // handle null setting
488          *     } else {
489          *        // get and use non-null setting
490          *     }
491          * } else {
492          *     // handle entirely unset path
493          * }
494          * </code></pre>
495          *
496          * <p> However, the usual thing is to allow entirely unset
497          * paths to be a bug that throws an exception (because you set
498          * a default in your <code>reference.conf</code>), so in that
499          * case it's OK to call {@link #get_is_null(string)} without
500          * checking <code>has_path_or_null</code> first.
501          *
502          * <p>
503          * Note that path expressions have a syntax and sometimes require quoting
504          * (see {@link config_util#join_path} and {@link config_util#split_path}).
505          *
506          * @param path
507          *            the path expression
508          * @return true if a value is present at the path, even if the value is null
509          */
510         virtual bool has_path_or_null(std::string const& path) const;
511 
512         /**
513          * Returns true if the {@code Config}'s root object contains no key-value
514          * pairs.
515          *
516          * @return true if the configuration is empty
517          */
518         virtual bool is_empty() const;
519 
520         /**
521          * Returns the set of path-value pairs, excluding any null values, found by
522          * recursing {@link #root() the root object}. Note that this is very
523          * different from <code>root().entry_set()</code> which returns the set of
524          * immediate-child keys in the root object and includes null values.
525          * <p>
526          * Entries contain <em>path expressions</em> meaning there may be quoting
527          * and escaping involved. Parse path expressions with
528          * {@link config_util#split_path}.
529          * <p>
530          * Because a <code>config</code> is conceptually a single-level map from
531          * paths to values, there will not be any {@link config_object} values in the
532          * entries (that is, all entries represent leaf nodes). Use
533          * {@link config_object} rather than <code>config</code> if you want a tree.
534          * (OK, this is a slight lie: <code>config</code> entries may contain
535          * {@link config_list} and the lists may contain objects. But no objects are
536          * directly included as entry values.)
537          *
538          * @return set of paths with non-null values, built up by recursing the
539          *         entire tree of {@link config_object} and creating an entry for
540          *         each leaf value.
541          */
542         virtual std::set<std::pair<std::string, std::shared_ptr<const config_value>>> entry_set() const;
543 
544         /**
545          * Checks whether a value is set to null at the given path,
546          * but throws an exception if the value is entirely
547          * unset. This method will not throw if {@link
548          * #has_path_or_null(string)} returned true for the same path, so
549          * to avoid any possible exception check
550          * <code>has_path_or_null()</code> first.  However, an exception
551          * for unset paths will usually be the right thing (because a
552          * <code>reference.conf</code> should exist that has the path
553          * set, the path should never be unset unless something is
554          * broken).
555          *
556          * <p>
557          * Note that path expressions have a syntax and sometimes require quoting
558          * (see {@link config_util#join_path} and {@link config_util#split_path}).
559          *
560          * @param path
561          *            the path expression
562          * @return true if the value exists and is null, false if it
563          * exists and is not null
564          */
565         virtual bool get_is_null(std::string const& path) const;
566 
567         virtual bool get_bool(std::string const& path) const;
568         virtual int get_int(std::string const& path) const;
569         virtual int64_t get_long(std::string const& path) const;
570         virtual double get_double(std::string const& path) const;
571         virtual std::string get_string(std::string const& path) const;
572         virtual std::shared_ptr<const config_object> get_object(std::string const& path) const;
573         virtual shared_config get_config(std::string const& path) const;
574         virtual unwrapped_value get_any_ref(std::string const& path) const;
575         virtual std::shared_ptr<const config_value> get_value(std::string const& path) const;
576 
577         template<typename T>
get_homogeneous_unwrapped_list(std::string const & path) const578         std::vector<T> get_homogeneous_unwrapped_list(std::string const& path) const {
579             auto list = boost::get<std::vector<unwrapped_value>>(get_list(path)->unwrapped());
580             std::vector<T> T_list;
581             for (auto item : list) {
582                 try {
583                     T_list.push_back(boost::get<T>(item));
584                 } catch (boost::bad_get &ex) {
585                     throw config_exception(leatherman::locale::format("The list did not contain only the desired type."));
586                 }
587             }
588             return T_list;
589         }
590 
591         virtual shared_list get_list(std::string const& path) const;
592         virtual std::vector<bool> get_bool_list(std::string const& path) const;
593         virtual std::vector<int> get_int_list(std::string const& path) const;
594         virtual std::vector<int64_t> get_long_list(std::string const& path) const;
595         virtual std::vector<double> get_double_list(std::string const& path) const;
596         virtual std::vector<std::string> get_string_list(std::string const& path) const;
597         virtual std::vector<shared_object> get_object_list(std::string const& path) const;
598         virtual std::vector<shared_config> get_config_list(std::string const& path) const;
599 
600         // TODO: memory parsing
601 
602         /**
603          * Gets a value as an integer number of the specified units.
604          * If the result would have a fractional part, the number is truncated.
605          * Correctly handles durations within the range +/-2^63 seconds.
606          * @param path the path to the time value
607          * @param unit the units of the number returned
608          * @return a 64-bit integer representing the value converted to the requested units
609          */
610         virtual int64_t get_duration(std::string const& path, time_unit unit) const;
611 
612         /**
613          * Clone the config with only the given path (and its children) retained;
614          * all sibling paths are removed.
615          * <p>
616          * Note that path expressions have a syntax and sometimes require quoting
617          * (see {@link config_util#join_path} and {@link config_util#split_path}).
618          *
619          * @param path
620          *            path to keep
621          * @return a copy of the config minus all paths except the one specified
622          */
623         virtual shared_config with_only_path(std::string const& path) const;
624 
625         /**
626          * Clone the config with the given path removed.
627          * <p>
628          * Note that path expressions have a syntax and sometimes require quoting
629          * (see {@link config_util#join_path} and {@link config_util#split_path}).
630          *
631          * @param path
632          *            path expression to remove
633          * @return a copy of the config minus the specified path
634          */
635         virtual shared_config without_path(std::string const& path) const;
636 
637         /**
638          * Places the config inside another {@code config} at the given path.
639          * <p>
640          * Note that path expressions have a syntax and sometimes require quoting
641          * (see {@link config_util#join_path} and {@link config_util#split_path}).
642          *
643          * @param path
644          *            path expression to store this config at.
645          * @return a {@code config} instance containing this config at the given
646          *         path.
647          */
648         virtual shared_config at_path(std::string const& path) const;
649 
650         /**
651          * Places the config inside a {@code config} at the given key. See also
652          * at_path(). Note that a key is NOT a path expression (see
653          * {@link config_util#join_path} and {@link config_util#split_path}).
654          *
655          * @param key
656          *            key to store this config at.
657          * @return a {@code config} instance containing this config at the given
658          *         key.
659          */
660         virtual shared_config at_key(std::string const& key) const;
661 
662         /**
663          * Returns a {@code config} based on this one, but with the given path set
664          * to the given value. Does not modify this instance (since it's immutable).
665          * If the path already has a value, that value is replaced. To remove a
666          * value, use withoutPath().
667          * <p>
668          * Note that path expressions have a syntax and sometimes require quoting
669          * (see {@link config_util#join_path} and {@link config_util#split_path}).
670          *
671          * @param path
672          *            path expression for the value's new location
673          * @param value
674          *            value at the new path
675          * @return the new instance with the new map entry
676          */
677         virtual shared_config with_value(std::string const& path, std::shared_ptr<const config_value> value) const;
678 
679         bool operator==(config const& other) const;
680 
681         config(shared_object object);
682 
683         static shared_object env_variables_as_config_object();
684 
685     protected:
686         shared_value find(std::string const& path_expression, config_value::type expected) const;
687         shared_value find(path path_expression, config_value::type expected, path original_path) const;
688         shared_value find(path path_expression, config_value::type expected) const;
689         shared_config at_key(shared_origin origin, std::string const& key) const;
690 
691         static shared_includer default_includer();
692 
693         // TODO: memory and duration parsing
694 
695     private:
696         /**
697          * Parses a duration string. If no units are specified in the string, it is assumed to be in
698          * milliseconds.
699          * Valid suffixes include ns, us, ms, s, m, h, and d. The units can also be specified as complete
700          * words (e.g. "seconds").
701          * @param input the string to parse
702          * @param origin_for_exception the origin of the value being parsed
703          * @param path_for_exception the path to the value being parsed
704          * @return the value parsed as a time_duration
705          */
706         static duration parse_duration(std::string input, shared_origin origin_for_exception, std::string path_for_exception);
707 
708         static duration convert(int64_t number, time_unit units);
709         static duration convert(double number, time_unit units);
710         static time_unit get_units(std::string const& unit_string);
711         duration get_duration(std::string const& path) const;
712 
713         shared_value has_path_peek(std::string const& path_expression) const;
714         shared_value peek_path(path desired_path) const;
715 
716         static void find_paths(std::set<std::pair<std::string, std::shared_ptr<const config_value>>>& entries,
717                                path parent, shared_object obj);
718         static shared_value throw_if_null(shared_value v, config_value::type expected, path original_path);
719         static shared_value find_key(shared_object self, std::string const& key,
720                                      config_value::type expected, path original_path);
721         static shared_value find_key_or_null(shared_object self, std::string const& key,
722                                              config_value::type expected, path original_path);
723         static shared_value find_or_null(shared_object self, path desired_path,
724                                          config_value::type expected, path original_path);
725         shared_value find_or_null(std::string const& path_expression, config_value::type expected) const;
726         shared_value find_or_null(path path_expression, config_value::type expected, path original_path) const;
727 
728         shared_object _object;
729     };
730 
731     template<>
732     std::vector<int64_t> config::get_homogeneous_unwrapped_list(std::string const& path) const;
733 
734 }  // namespace hocon
735