VirtualBox

source: vbox/trunk/include/VBox/settings.h@ 8006

Last change on this file since 8006 was 7388, checked in by vboxsync, 16 years ago

Main/Settings: Perform conversion in a loop to allow for multi-step version to version updates (e.g. v1.0->v1.1->v1.2) which saves from patching all previous transformation rules when on every version change but still makes it possible to update from too old versions to the most recent one.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 47.4 KB
Line 
1/** @file
2 * Settings File Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007 innotek GmbH
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License as published by the Free Software Foundation,
12 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
13 * distribution. VirtualBox OSE is distributed in the hope that it will
14 * be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#ifndef ___VBox_settings_h
18#define ___VBox_settings_h
19
20#include <iprt/cdefs.h>
21#include <iprt/cpputils.h>
22#include <iprt/string.h>
23
24#include <list>
25#include <memory>
26#include <limits>
27
28/* these conflict with numeric_digits<>::min and max */
29#undef min
30#undef max
31
32#include <iprt/assert.h>
33#include <iprt/string.h>
34#include <iprt/mem.h>
35#include <iprt/time.h>
36
37#include <stdarg.h>
38
39
40/** @defgroup grp_settings Settings File Manipulation API
41 * @{
42 *
43 * The Settings File Manipulation API allows to maintain a configuration file
44 * that contains "name-value" pairs grouped under named keys which are in turn
45 * organized in a hierarchical tree-like structure:
46 *
47 * @code
48 * <RootKey>
49 * <Key1 attr1="value" attr2=""/>
50 * <Key2 attr1="value">
51 * <SubKey1>SubKey1_Value</SubKey1>
52 * <SubKey2 attr1="value">SubKey2_Value</SubKey2>
53 * Key2_Value
54 * </Key2>
55 * </RootKey>
56 * @endcode
57 *
58 * All strings this API manipulates with are zero-terminated arrays of @c char
59 * in UTF-8 encoding. Strings returned by the API are owned by the API unless
60 * explicitly stated otherwise. Strings passed to the API are accessed by the
61 * API only during the given API call unless explicitly stated otherwise. If
62 * necessary, the API will make a copy of the supplied string.
63 *
64 * Error reprting is perfomed using C++ exceptions. All exceptions thrown by
65 * this API are derived from settings::Error. This doesn't cover exceptions
66 * that may be thrown by third-party library calls made by this API.
67 *
68 * All public classes represented by this API that support copy operations
69 * (i.e. may be created or assigned from other instsances of the same class),
70 * such as Key and Value classes, implement shallow copies and use this mode by
71 * default. It means two things:
72 *
73 * 1. Instances of these classes can be freely copied around and used as return
74 * values. All copies will share the same internal data block (using the
75 * reference counting technique) so that the copy operation is cheap, both
76 * in terms of memory and speed.
77 *
78 * 2. Since copied instances share the same data, an attempt to change data in
79 * the original will be reflected in all existing copies.
80 *
81 * Making deep copies or detaching the existing shallow copy from its original
82 * is not yet supported.
83 *
84 * Due to some (not propely studied) libxml2 limitations, the Settings File
85 * API is not thread-safe. Therefore, the API caller must provide
86 * serialization for threads using this API simultaneously. Note though that
87 * if the libxml2 library is (even imlicitly) used on some other thread which
88 * doesn't use this API (e.g. third-party code), it may lead to resource
89 * conflicts (followed by crashes, memory corruption etc.). A proper solution
90 * for these conflicts is to be found.
91 *
92 * In order to load a settings file the program creates a TreeBackend instance
93 * using one of the specific backends (e.g. XmlTreeBackend) and then passes an
94 * Input stream object (e.g. File or MemoryBuf) to the TreeBackend::read()
95 * method to parse the stream and build the settings tree. On success, the
96 * program uses the TreeBackend::rootKey() method to access the root key of
97 * the settings tree. The root key provides access to the whole tree of
98 * settings through the methods of the Key class which allow to read, change
99 * and create new key values. Below is an example that uses the XML backend to
100 * load the settings tree, then modifies it and then saves the modifications.
101 *
102 * @code
103 using namespace settings;
104
105 try
106 {
107 File file (File::ReadWrite, "myfile.xml");
108 XmlTreeBackend tree;
109
110 // load the tree, parse it and validate using the XML schema
111 tree.read (aFile, "myfile.xsd", XmlTreeBackend::Read_AddDefaults);
112
113 // get the root key
114 Key root = tree.key();
115 printf ("root=%s\n", root.name());
116
117 // enumerate all child keys of the root key named Foo
118 Key::list children = root.keys ("Foo");
119 for (Key::list::const_iterator it = children.begin();
120 it != children.end();
121 ++ it)
122 {
123 // get the "level" attribute
124 int level = (*it).value <int> ("level");
125 if (level > 5)
126 {
127 // if so, create a "Bar" key if it doesn't exist yet
128 Key bar = (*it).createKey ("Bar");
129 // set the "date" attribute
130 RTTIMESPEC now;
131 RTTimeNow (&now);
132 bar.setValue <RTTIMESPEC> ("date", now);
133 }
134 else if (level < 2)
135 {
136 // if its below 2, delete the whole "Foo" key
137 (*it).zap();
138 }
139 }
140
141 // save the tree on success (the second try is to distinguish between
142 // stream load and save errors)
143 try
144 {
145 aTree.write (aFile);
146 }
147 catch (const EIPRTFailure &err)
148 {
149 // this is an expected exception that may happen in case of stream
150 // read or write errors
151 printf ("Could not save the settings file '%s' (%Vrc)");
152 file.uri(), err.rc());
153
154 return FAILURE;
155 }
156
157 return SUCCESS;
158 }
159 catch (const EIPRTFailure &err)
160 {
161 // this is an expected exception that may happen in case of stream
162 // read or write errors
163 printf ("Could not load the settings file '%s' (%Vrc)");
164 file.uri(), err.rc());
165 }
166 catch (const XmlTreeBackend::Error &err)
167 {
168 // this is an XmlTreeBackend specific exception exception that may
169 // happen in case of XML parse or validation errors
170 printf ("Could not load the settings file '%s'.\n%s"),
171 file.uri(), err.what() ? err.what() : "Unknown error");
172 }
173 catch (const std::exception &err)
174 {
175 // the rest is unexpected (e.g. should not happen unless you
176 // specifically wish so for some reason and therefore allow for a
177 // situation that may throw one of these from within the try block
178 // above)
179 AssertMsgFailed ("Unexpected exception '%s' (%s)\n",
180 typeid (err).name(), err.what());
181 catch (...)
182 {
183 // this is even more unexpected, and no any useful info here
184 AssertMsgFailed ("Unexpected exception\n");
185 }
186
187 return FAILURE;
188 * @endcode
189 *
190 * Note that you can get a raw (string) value of the attribute using the
191 * Key::stringValue() method but often it's simpler and better to use the
192 * templated Key::value<>() method that can convert the string to a value of
193 * the given type for you (and throw exceptions when the converison is not
194 * possible). Similarly, the Key::setStringValue() methid is used to set a raw
195 * string value and there is a templated Key::setValue<>() method to set a
196 * typed value which will implicitly convert it to a string.
197 *
198 * Currently, types supported by Key::value<>() and Key::setValue<>() include
199 * all C and IPRT integer types, bool and RTTIMESPEC (represented as isoDate
200 * in XML). You can always add support for your own types by creating
201 * additional specializations of the FromString<>() and ToString<>() templates
202 * in the settings namespace (see the real examples in this header).
203 *
204 * See individual funciton, class and method descriptions to get more details
205 * on the Settings File Manipulation API.
206 */
207
208#ifndef IN_RING3
209# error "There are no settings APIs available in Ring-0 Context!"
210#else /* IN_RING3 */
211
212/** @def IN_VBOXSETTINGS_R3
213 * Used to indicate whether we're inside the same link module as the
214 * XML Settings File Manipulation API.
215 *
216 * @todo should go to a separate common include together with VBOXXML2_CLASS
217 * once there becomes more than one header in the VBoxXML2 library.
218 */
219#ifdef IN_VBOXSETTINGS_R3
220# define VBOXSETTINGS_CLASS DECLEXPORT_CLASS
221#else
222# define VBOXSETTINGS_CLASS DECLIMPORT_CLASS
223#endif
224
225/*
226 * Shut up MSVC complaining that auto_ptr[_ref] template instantiations (as a
227 * result of private data member declarations of some classes below) need to
228 * be exported too to in order to be accessible by clients. I don't
229 *
230 * The alternative is to instantiate a template before the data member
231 * declaration with the VBOXSETTINGS_CLASS prefix, but the standard disables
232 * explicit instantiations in a foreign namespace. However, a declaration
233 * like:
234 *
235 * template class VBOXSETTINGS_CLASS std::auto_ptr <Data>;
236 *
237 * right before the member declaration makes MSVC happy too, but this is not a
238 * valid C++ construct (and G++ spits it out). So, for now we just disable the
239 * warning and will come back to this problem one dat later.
240 *
241 * We also disable another warning (4275) saying that a DLL-exported class
242 * inherits form a non-DLL-exported one (e.g. settings::ENoMemory ->
243 * std::bad_alloc). I can't get how it can harm yet.
244 */
245#if defined(_MSC_VER)
246#pragma warning (disable:4251)
247#pragma warning (disable:4275)
248#endif
249
250/* Forwards */
251typedef struct _xmlParserInput xmlParserInput;
252typedef xmlParserInput *xmlParserInputPtr;
253typedef struct _xmlParserCtxt xmlParserCtxt;
254typedef xmlParserCtxt *xmlParserCtxtPtr;
255typedef struct _xmlError xmlError;
256typedef xmlError *xmlErrorPtr;
257
258/**
259 * Settings File Manipulation API namespace.
260 */
261namespace settings
262{
263
264// Helpers
265//////////////////////////////////////////////////////////////////////////////
266
267/**
268 * Temporary holder for the formatted string.
269 *
270 * Instances of this class are used for passing the formatted string as an
271 * argument to an Error constructor or to another function that takes
272 * <tr>const char *</tr> and makes a copy of the string it points to.
273 */
274class VBOXSETTINGS_CLASS FmtStr
275{
276public:
277
278 /**
279 * Creates a formatted string using the format string and a set of
280 * printf-like arguments.
281 */
282 FmtStr (const char *aFmt, ...)
283 {
284 va_list args;
285 va_start (args, aFmt);
286 RTStrAPrintfV (&mStr, aFmt, args);
287 va_end (args);
288 }
289
290 ~FmtStr() { RTStrFree (mStr); }
291
292 operator const char *() { return mStr; }
293
294private:
295
296 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (FmtStr)
297
298 char *mStr;
299};
300
301// Exceptions
302//////////////////////////////////////////////////////////////////////////////
303
304/**
305 * Base exception class.
306 */
307class VBOXSETTINGS_CLASS Error : public std::exception
308{
309public:
310
311 Error (const char *aMsg = NULL)
312 : m (aMsg ? Str::New (aMsg) : NULL) {}
313
314 virtual ~Error() throw() {}
315
316 void setWhat (const char *aMsg) { m = aMsg ? Str::New (aMsg) : NULL; }
317
318 const char *what() const throw() { return m.is_null() ? NULL : m->str; }
319
320private:
321
322 /** smart string with support for reference counting */
323 struct Str
324 {
325 size_t ref() { return ++ refs; }
326 size_t unref() { return -- refs; }
327
328 size_t refs;
329 char str [1];
330
331 static Str *New (const char *aStr)
332 {
333 Str *that = (Str *) RTMemAllocZ (sizeof (Str) + strlen (aStr));
334 AssertReturn (that, NULL);
335 strcpy (that->str, aStr);
336 return that;
337 }
338
339 void operator delete (void *that, size_t) { RTMemFree (that); }
340 };
341
342 stdx::auto_ref_ptr <Str> m;
343};
344
345class VBOXSETTINGS_CLASS LogicError : public Error
346{
347public:
348
349 LogicError (const char *aMsg = NULL) : Error (aMsg) {}
350
351 LogicError (RT_SRC_POS_DECL)
352 {
353 char *msg = NULL;
354 RTStrAPrintf (&msg, "In '%s', '%s' at #%d",
355 pszFunction, pszFile, iLine);
356 setWhat (msg);
357 RTStrFree (msg);
358 }
359};
360
361class VBOXSETTINGS_CLASS RuntimeError : public Error
362{
363public:
364
365 RuntimeError (const char *aMsg = NULL) : Error (aMsg) {}
366};
367
368// Logical errors
369//////////////////////////////////////////////////////////////////////////////
370
371class VBOXSETTINGS_CLASS ENotImplemented : public LogicError
372{
373public:
374
375 ENotImplemented (const char *aMsg = NULL) : LogicError (aMsg) {}
376 ENotImplemented (RT_SRC_POS_DECL) : LogicError (RT_SRC_POS_ARGS) {}
377};
378
379class VBOXSETTINGS_CLASS EInvalidArg : public LogicError
380{
381public:
382
383 EInvalidArg (const char *aMsg = NULL) : LogicError (aMsg) {}
384 EInvalidArg (RT_SRC_POS_DECL) : LogicError (RT_SRC_POS_ARGS) {}
385};
386
387class VBOXSETTINGS_CLASS ENoKey : public LogicError
388{
389public:
390
391 ENoKey (const char *aMsg = NULL) : LogicError (aMsg) {}
392};
393
394class VBOXSETTINGS_CLASS ENoValue : public LogicError
395{
396public:
397
398 ENoValue (const char *aMsg = NULL) : LogicError (aMsg) {}
399};
400
401// Runtime errors
402//////////////////////////////////////////////////////////////////////////////
403
404class VBOXSETTINGS_CLASS ENoMemory : public RuntimeError, public std::bad_alloc
405{
406public:
407
408 ENoMemory (const char *aMsg = NULL) : RuntimeError (aMsg) {}
409 virtual ~ENoMemory() throw() {}
410};
411
412class VBOXSETTINGS_CLASS EIPRTFailure : public RuntimeError
413{
414public:
415
416 EIPRTFailure (const char *aMsg = NULL) : RuntimeError (aMsg) {}
417
418 EIPRTFailure (int aRC) : mRC (aRC) {}
419 int rc() const { return mRC; }
420
421private:
422
423 int mRC;
424};
425
426class VBOXSETTINGS_CLASS ENoConversion : public RuntimeError
427{
428public:
429
430 ENoConversion (const char *aMsg = NULL) : RuntimeError (aMsg) {}
431};
432
433// string -> type conversions
434//////////////////////////////////////////////////////////////////////////////
435
436/** @internal
437 * Helper for the FromString() template, doesn't need to be called directly.
438 */
439DECLEXPORT (uint64_t) FromStringInteger (const char *aValue, bool aSigned,
440 int aBits, uint64_t aMin, uint64_t aMax);
441
442/**
443 * Generic template function to perform a conversion of an UTF-8 string to an
444 * arbitrary value of type @a T.
445 *
446 * This generic template is implenented only for 8-, 16-, 32- and 64- bit
447 * signed and unsigned integers where it uses RTStrTo[U]Int64() to perform the
448 * conversion. For all other types it throws an ENotImplemented
449 * exception. Individual template specializations for known types should do
450 * the conversion job.
451 *
452 * If the conversion is not possible (for example the string format is wrong
453 * or meaningless for the given type), this template will throw an
454 * ENoConversion exception. All specializations must do the same.
455 *
456 * If the @a aValue argument is NULL, this method will throw an ENoValue
457 * exception. All specializations must do the same.
458 *
459 * @param aValue Value to convert.
460 *
461 * @return Result of conversion.
462 */
463template <typename T>
464T FromString (const char *aValue)
465{
466 if (std::numeric_limits <T>::is_integer)
467 {
468 bool sign = std::numeric_limits <T>::is_signed;
469 int bits = std::numeric_limits <T>::digits + (sign ? 1 : 0);
470
471 return (T) FromStringInteger (aValue, sign, bits,
472 (uint64_t) std::numeric_limits <T>::min(),
473 (uint64_t) std::numeric_limits <T>::max());
474 }
475
476 throw ENotImplemented (RT_SRC_POS);
477}
478
479/**
480 * Specialization of FromString for bool.
481 *
482 * Converts "true", "yes", "on" to true and "false", "no", "off" to false.
483 */
484template<> DECLEXPORT (bool) FromString <bool> (const char *aValue);
485
486/**
487 * Specialization of FromString for RTTIMESPEC.
488 *
489 * Converts the date in ISO format (<YYYY>-<MM>-<DD>T<hh>:<mm>:<ss>[timezone])
490 * to a RTTIMESPEC value. Currently, the timezone must always be Z (UTC).
491 */
492template<> DECLEXPORT (RTTIMESPEC) FromString <RTTIMESPEC> (const char *aValue);
493
494/**
495 * Converts a string of hex digits to memory bytes.
496 *
497 * @param aValue String to convert.
498 * @param aLen Where to store the length of the returned memory
499 * block (may be NULL).
500 *
501 * @return Result of conversion (a block of @a aLen bytes).
502 */
503DECLEXPORT (stdx::char_auto_ptr) FromString (const char *aValue, size_t *aLen);
504
505// type -> string conversions
506//////////////////////////////////////////////////////////////////////////////
507
508/** @internal
509 * Helper for the ToString() template, doesn't need to be called directly.
510 */
511DECLEXPORT (stdx::char_auto_ptr)
512ToStringInteger (uint64_t aValue, unsigned int aBase,
513 bool aSigned, int aBits);
514
515/**
516 * Generic template function to perform a conversion of an arbitrary value to
517 * an UTF-8 string.
518 *
519 * This generic template is implemented only for 8-, 16-, 32- and 64- bit
520 * signed and unsigned integers where it uses RTStrFormatNumber() to perform
521 * the conversion. For all other types it throws an ENotImplemented
522 * exception. Individual template specializations for known types should do
523 * the conversion job. If the conversion is not possible (for example the
524 * given value doesn't have a string representation), the relevant
525 * specialization should throw an ENoConversion exception.
526 *
527 * If the @a aValue argument's value would convert to a NULL string, this
528 * method will throw an ENoValue exception. All specializations must do the
529 * same.
530 *
531 * @param aValue Value to convert.
532 * @param aExtra Extra flags to define additional formatting. In case of
533 * integer types, it's the base used for string representation.
534 *
535 * @return Result of conversion.
536 */
537template <typename T>
538stdx::char_auto_ptr ToString (const T &aValue, unsigned int aExtra = 0)
539{
540 if (std::numeric_limits <T>::is_integer)
541 {
542 bool sign = std::numeric_limits <T>::is_signed;
543 int bits = std::numeric_limits <T>::digits + (sign ? 1 : 0);
544
545 return ToStringInteger (aValue, aExtra, sign, bits);
546 }
547
548 throw ENotImplemented (RT_SRC_POS);
549}
550
551/**
552 * Specialization of ToString for bool.
553 *
554 * Converts true to "true" and false to "false". @a aExtra is not used.
555 */
556template<> DECLEXPORT (stdx::char_auto_ptr)
557ToString <bool> (const bool &aValue, unsigned int aExtra);
558
559/**
560 * Specialization of ToString for RTTIMESPEC.
561 *
562 * Converts the RTTIMESPEC value to the date string in ISO format
563 * (<YYYY>-<MM>-<DD>T<hh>:<mm>:<ss>[timezone]). Currently, the timezone will
564 * always be Z (UTC).
565 *
566 * @a aExtra is not used.
567 */
568template<> DECLEXPORT (stdx::char_auto_ptr)
569ToString <RTTIMESPEC> (const RTTIMESPEC &aValue, unsigned int aExtra);
570
571/**
572 * Converts memory bytes to a null-terminated string of hex values.
573 *
574 * @param aData Pointer to the memory block.
575 * @param aLen Length of the memory block.
576 *
577 * @return Result of conversion.
578 */
579DECLEXPORT (stdx::char_auto_ptr) ToString (const void *aData, size_t aLen);
580
581// the rest
582//////////////////////////////////////////////////////////////////////////////
583
584/**
585 * The Key class represents a settings key.
586 *
587 * Every settings key has a name and zero or more uniquely named values
588 * (attributes). There is a special attribute with a NULL name that is called
589 * a key value.
590 *
591 * Besides values, settings keys may contain other settings keys. This way,
592 * settings keys form a tree-like (or a directory-like) hierarchy of keys. Key
593 * names do not need to be unique even if they belong to the same parent key
594 * which allows to have an array of keys of the same name.
595 *
596 * @note Key and Value objects returned by methods of the Key and TreeBackend
597 * classes are owned by the given TreeBackend instance and may refer to data
598 * that becomes invalid when this TreeBackend instance is destroyed.
599 */
600class VBOXSETTINGS_CLASS Key
601{
602public:
603
604 typedef std::list <Key> List;
605
606 /**
607 * Key backend interface used to perform actual key operations.
608 *
609 * This interface is implemented by backends that provide specific ways of
610 * storing settings keys.
611 */
612 class VBOXSETTINGS_CLASS Backend : public stdx::auto_ref
613 {
614 public:
615
616 /** Performs the Key::name() function. */
617 virtual const char *name() const = 0;
618
619 /** Performs the Key::setName() function. */
620 virtual void setName (const char *aName) = 0;
621
622 /** Performs the Key::stringValue() function. */
623 virtual const char *value (const char *aName) const = 0;
624
625 /** Performs the Key::setStringValue() function. */
626 virtual void setValue (const char *aName, const char *aValue) = 0;
627
628 /** Performs the Key::keys() function. */
629 virtual List keys (const char *aName = NULL) const = 0;
630
631 /** Performs the Key::findKey() function. */
632 virtual Key findKey (const char *aName) const = 0;
633
634 /** Performs the Key::appendKey() function. */
635 virtual Key appendKey (const char *aName) = 0;
636
637 /** Performs the Key::zap() function. */
638 virtual void zap() = 0;
639
640 /**
641 * Returns an opaque value that uniquely represents the position of
642 * this key on the tree which is used to compare two keys. Two or more
643 * keys may return the same value only if they actually represent the
644 * same key (i.e. they have the same list of parents and children).
645 */
646 virtual void *position() const = 0;
647 };
648
649 /**
650 * Creates a new key object. If @a aBackend is @c NULL then a null key is
651 * created.
652 *
653 * Regular API users should never need to call this method with something
654 * other than NULL argument (which is the default).
655 *
656 * @param aBackend Key backend to use.
657 */
658 Key (Backend *aBackend = NULL) : m (aBackend) {}
659
660 /**
661 * Returns @c true if this key is null.
662 */
663 bool isNull() const { return m.is_null(); }
664
665 /**
666 * Makes this object a null key.
667 *
668 * Note that as opposed to #zap(), this methid does not delete the key from
669 * the list of children of its parent key.
670 */
671 void setNull() { m = NULL; }
672
673 /**
674 * Returns the name of this key.
675 * Returns NULL if this object a null (uninitialized) key.
676 */
677 const char *name() const { return m.is_null() ? NULL : m->name(); }
678
679 /**
680 * Sets the name of this key.
681 *
682 * @param aName New key name.
683 */
684 void setName (const char *aName) { if (!m.is_null()) m->setName (aName); }
685
686 /**
687 * Returns the value of the attribute with the given name as an UTF-8
688 * string. Returns @c NULL if there is no attribute with the given name.
689 *
690 * @param aName Name of the attribute. NULL may be used to
691 * get the key value.
692 */
693 const char *stringValue (const char *aName) const
694 {
695 return m.is_null() ? NULL : m->value (aName);
696 }
697
698 /**
699 * Sets the value of the attribute with the given name from an UTF-8
700 * string. This method will do a copy of the supplied @a aValue string.
701 *
702 * @param aName Name of the attribute. NULL may be used to
703 * set the key value.
704 * @param aValue New value of the attribute. NULL may be used to
705 * delete the value instead of setting it.
706 */
707 void setStringValue (const char *aName, const char *aValue)
708 {
709 if (!m.is_null()) m->setValue (aName, aValue);
710 }
711
712 /**
713 * Returns the value of the attribute with the given name as an object of
714 * type @a T. Throws ENoValue if there is no attribute with the given
715 * name.
716 *
717 * This function calls #stringValue() to get the string representation of
718 * the attribute and then calls the FromString() template to convert this
719 * string to a value of the given type.
720 *
721 * @param aName Name of the attribute. NULL may be used to
722 * get the key value.
723 */
724 template <typename T>
725 T value (const char *aName) const
726 {
727 try
728 {
729 return FromString <T> (stringValue (aName));
730 }
731 catch (const ENoValue &)
732 {
733 throw ENoValue (FmtStr ("No such attribute '%s'", aName));
734 }
735 }
736
737 /**
738 * Returns the value of the attribute with the given name as an object of
739 * type @a T. Returns the given default value if there is no attribute
740 * with the given name.
741 *
742 * This function calls #stringValue() to get the string representation of
743 * the attribute and then calls the FromString() template to convert this
744 * string to a value of the given type.
745 *
746 * @param aName Name of the attribute. NULL may be used to
747 * get the key value.
748 * @param aDefault Default value to return for the missing attribute.
749 */
750 template <typename T>
751 T valueOr (const char *aName, const T &aDefault) const
752 {
753 try
754 {
755 return FromString <T> (stringValue (aName));
756 }
757 catch (const ENoValue &)
758 {
759 return aDefault;
760 }
761 }
762
763 /**
764 * Sets the value of the attribute with the given name from an object of
765 * type @a T. This method will do a copy of data represented by @a aValue
766 * when necessary.
767 *
768 * This function converts the given value to a string using the ToString()
769 * template and then calls #setStringValue().
770 *
771 * @param aName Name of the attribute. NULL may be used to
772 * set the key value.
773 * @param aValue New value of the attribute.
774 * @param aExtra Extra field used by some types to specify additional
775 * details for storing the value as a string (such as the
776 * base for decimal numbers).
777 */
778 template <typename T>
779 void setValue (const char *aName, const T &aValue, unsigned int aExtra = 0)
780 {
781 try
782 {
783 stdx::char_auto_ptr value = ToString (aValue, aExtra);
784 setStringValue (aName, value.get());
785 }
786 catch (const ENoValue &)
787 {
788 throw ENoValue (FmtStr ("No value for attribute '%s'", aName));
789 }
790 }
791
792 /**
793 * Sets the value of the attribute with the given name from an object of
794 * type @a T. If the value of the @a aValue object equals to the value of
795 * the given @a aDefault object, then the attribute with the given name
796 * will be deleted instead of setting its value to @a aValue.
797 *
798 * This function converts the given value to a string using the ToString()
799 * template and then calls #setStringValue().
800 *
801 * @param aName Name of the attribute. NULL may be used to
802 * set the key value.
803 * @param aValue New value of the attribute.
804 * @param aDefault Default value to compare @a aValue to.
805 * @param aExtra Extra field used by some types to specify additional
806 * details for storing the value as a string (such as the
807 * base for decimal numbers).
808 */
809 template <typename T>
810 void setValueOr (const char *aName, const T &aValue, const T &aDefault,
811 unsigned int aExtra = 0)
812 {
813 if (aValue == aDefault)
814 zapValue (aName);
815 else
816 setValue <T> (aName, aValue, aExtra);
817 }
818
819 /**
820 * Deletes the value of the attribute with the given name.
821 * Shortcut to <tt>setStringValue(aName, NULL)</tt>.
822 */
823 void zapValue (const char *aName) { setStringValue (aName, NULL); }
824
825 /**
826 * Returns the key value.
827 * Shortcut to <tt>stringValue (NULL)</tt>.
828 */
829 const char *keyStringValue() const { return stringValue (NULL); }
830
831 /**
832 * Sets the key value.
833 * Shortcut to <tt>setStringValue (NULL, aValue)</tt>.
834 */
835 void setKeyStringValue (const char *aValue) { setStringValue (NULL, aValue); }
836
837 /**
838 * Returns the key value.
839 * Shortcut to <tt>value (NULL)</tt>.
840 */
841 template <typename T>
842 T keyValue() const { return value <T> (NULL); }
843
844 /**
845 * Returns the key value or the given default if the key value is NULL.
846 * Shortcut to <tt>value (NULL)</tt>.
847 */
848 template <typename T>
849 T keyValueOr (const T &aDefault) const { return valueOr <T> (NULL, aDefault); }
850
851 /**
852 * Sets the key value.
853 * Shortcut to <tt>setValue (NULL, aValue, aExtra)</tt>.
854 */
855 template <typename T>
856 void setKeyValue (const T &aValue, unsigned int aExtra = 0)
857 {
858 setValue <T> (NULL, aValue, aExtra);
859 }
860
861 /**
862 * Sets the key value.
863 * Shortcut to <tt>setValueOr (NULL, aValue, aDefault)</tt>.
864 */
865 template <typename T>
866 void setKeyValueOr (const T &aValue, const T &aDefault,
867 unsigned int aExtra = 0)
868 {
869 setValueOr <T> (NULL, aValue, aDefault, aExtra);
870 }
871
872 /**
873 * Deletes the key value.
874 * Shortcut to <tt>zapValue (NULL)</tt>.
875 */
876 void zapKeyValue () { zapValue (NULL); }
877
878 /**
879 * Returns a list of all child keys named @a aName.
880 *
881 * If @a aname is @c NULL, returns a list of all child keys.
882 *
883 * @param aName Child key name to list.
884 */
885 List keys (const char *aName = NULL) const
886 {
887 return m.is_null() ? List() : m->keys (aName);
888 };
889
890 /**
891 * Returns the first child key with the given name.
892 *
893 * Throws ENoKey if no child key with the given name exists.
894 *
895 * @param aName Child key name.
896 */
897 Key key (const char *aName) const
898 {
899 Key key = findKey (aName);
900 if (key.isNull())
901 throw ENoKey (FmtStr ("No such key '%s'", aName));
902 return key;
903 }
904
905 /**
906 * Returns the first child key with the given name.
907 *
908 * As opposed to #key(), this method will not throw an exception if no
909 * child key with the given name exists, but return a null key instead.
910 *
911 * @param aName Child key name.
912 */
913 Key findKey (const char *aName) const
914 {
915 return m.is_null() ? Key() : m->findKey (aName);
916 }
917
918 /**
919 * Creates a key with the given name as a child of this key and returns it
920 * to the caller.
921 *
922 * If one or more child keys with the given name already exist, no new key
923 * is created but the first matching child key is returned.
924 *
925 * @param aName Name of the child key to create.
926 */
927 Key createKey (const char *aName)
928 {
929 Key key = findKey (aName);
930 if (key.isNull())
931 key = appendKey (aName);
932 return key;
933 }
934
935 /**
936 * Appends a key with the given name to the list of child keys of this key
937 * and returns the appended key to the caller.
938 *
939 * @param aName Name of the child key to create.
940 */
941 Key appendKey (const char *aName)
942 {
943 return m.is_null() ? Key() : m->appendKey (aName);
944 }
945
946 /**
947 * Deletes this key.
948 *
949 * The deleted key is removed from the list of child keys of its parent
950 * key and becomes a null object.
951 */
952 void zap()
953 {
954 if (!m.is_null())
955 {
956 m->zap();
957 setNull();
958 }
959 }
960
961 /**
962 * Compares this object with the given object and returns @c true if both
963 * represent the same key on the settings tree or if both are null
964 * objects.
965 *
966 * @param that Object to compare this object with.
967 */
968 bool operator== (const Key &that) const
969 {
970 return m == that.m ||
971 (!m.is_null() && !that.m.is_null() &&
972 m->position() == that.m->position());
973 }
974
975 /**
976 * Counterpart to operator==().
977 */
978 bool operator!= (const Key &that) const { return !operator== (that); }
979
980private:
981
982 stdx::auto_ref_ptr <Backend> m;
983
984 friend class TreeBackend;
985};
986
987/**
988 * The Stream class is a base class for I/O streams.
989 */
990class VBOXSETTINGS_CLASS Stream
991{
992public:
993
994 virtual ~Stream() {}
995
996 virtual const char *uri() const = 0;
997
998 /**
999 * Returns the current read/write position in the stream. The returned
1000 * position is a zero-based byte offset from the beginning of the file.
1001 *
1002 * Throws ENotImplemented if this operation is not implemented for the
1003 * given stream.
1004 */
1005 virtual uint64_t pos() const = 0;
1006
1007 /**
1008 * Sets the current read/write position in the stream.
1009 *
1010 * @param aPos Zero-based byte offset from the beginning of the stream.
1011 *
1012 * Throws ENotImplemented if this operation is not implemented for the
1013 * given stream.
1014 */
1015 virtual void setPos (uint64_t aPos) = 0;
1016};
1017
1018/**
1019 * The Input class represents an input stream.
1020 *
1021 * This input stream is used to read the settings tree from.
1022 * This is an abstract class that must be subclassed in order to fill it with
1023 * useful functionality.
1024 */
1025class VBOXSETTINGS_CLASS Input : virtual public Stream
1026{
1027public:
1028
1029 /**
1030 * Reads from the stream to the supplied buffer.
1031 *
1032 * @param aBuf Buffer to store read data to.
1033 * @param aLen Buffer length.
1034 *
1035 * @return Number of bytes read.
1036 */
1037 virtual int read (char *aBuf, int aLen) = 0;
1038};
1039
1040/**
1041 *
1042 */
1043class VBOXSETTINGS_CLASS Output : virtual public Stream
1044{
1045public:
1046
1047 /**
1048 * Writes to the stream from the supplied buffer.
1049 *
1050 * @param aBuf Buffer to write data from.
1051 * @param aLen Buffer length.
1052 *
1053 * @return Number of bytes written.
1054 */
1055 virtual int write (const char *aBuf, int aLen) = 0;
1056
1057 /**
1058 * Truncates the stream from the current position and upto the end.
1059 * The new file size will become exactly #pos() bytes.
1060 *
1061 * Throws ENotImplemented if this operation is not implemented for the
1062 * given stream.
1063 */
1064 virtual void truncate() = 0;
1065};
1066
1067/**
1068 * The TreeBackend class represents a storage backend used to read a settings
1069 * tree from and write it to a stream.
1070 *
1071 * @note All Key objects returned by any of the TreeBackend methods (and by
1072 * methods of returned Key objects) are owned by the given TreeBackend
1073 * instance. When this instance is destroyed, all Key objects become invalid
1074 * and an attempt to access Key data will cause the program crash.
1075 */
1076class VBOXSETTINGS_CLASS TreeBackend
1077{
1078public:
1079
1080 /**
1081 * Reads and parses the given input stream.
1082 *
1083 * On success, the previous settings tree owned by this backend (if any)
1084 * is deleted.
1085 *
1086 * The optional schema URI argument determines the name of the schema to
1087 * use for input validation. If the schema URI is NULL then the validation
1088 * is not performed. Note that you may set a custom input resolver if you
1089 * want to provide the input stream for the schema file (and for other
1090 * external entities) instead of letting the backend to read the specified
1091 * URI directly.
1092 *
1093 * This method will set the read/write position to the beginning of the
1094 * given stream before reading. After the stream has been successfully
1095 * parsed, the position will be set back to the beginning.
1096 *
1097 * @param aInput Input stream.
1098 * @param aSchema Schema URI to use for input stream validation.
1099 * @param aFlags Optional bit flags.
1100 */
1101 void read (Input &aInput, const char *aSchema = NULL, int aFlags = 0)
1102 {
1103 aInput.setPos (0);
1104 rawRead (aInput, aSchema, aFlags);
1105 aInput.setPos (0);
1106 }
1107
1108 /**
1109 * Reads and parses the given input stream in a raw fashion.
1110 *
1111 * This method doesn't set the stream position to the beginnign before and
1112 * after reading but instead leaves it as is in both cases. It's the
1113 * caller's responsibility to maintain the correct position.
1114 *
1115 * @see read()
1116 */
1117 virtual void rawRead (Input &aInput, const char *aSchema = NULL,
1118 int aFlags = 0) = 0;
1119
1120 /**
1121 * Writes the current settings tree to the given output stream.
1122 *
1123 * This method will set the read/write position to the beginning of the
1124 * given stream before writing. After the settings have been successfully
1125 * written to the stream, the stream will be truncated at the position
1126 * following the last byte written by this method anc ghd position will be
1127 * set back to the beginning.
1128 *
1129 * @param aOutput Output stream.
1130 */
1131 void write (Output &aOutput)
1132 {
1133 aOutput.setPos (0);
1134 rawWrite (aOutput);
1135 aOutput.truncate();
1136 aOutput.setPos (0);
1137 }
1138
1139 /**
1140 * Writes the current settings tree to the given output stream in a raw
1141 * fashion.
1142 *
1143 * This method doesn't set the stream position to the beginnign before and
1144 * after reading and doesn't truncate the stream, but instead leaves it as
1145 * is in both cases. It's the caller's responsibility to maintain the
1146 * correct position and perform truncation.
1147 *
1148 * @see write()
1149 */
1150 virtual void rawWrite (Output &aOutput) = 0;
1151
1152 /**
1153 * Deletes the current settings tree.
1154 */
1155 virtual void reset() = 0;
1156
1157 /**
1158 * Returns the root settings key.
1159 */
1160 virtual Key &rootKey() const = 0;
1161
1162protected:
1163
1164 static Key::Backend *GetKeyBackend (const Key &aKey) { return aKey.m.raw(); }
1165};
1166
1167//////////////////////////////////////////////////////////////////////////////
1168
1169/**
1170 * The File class is a stream implementation that reads from and writes to
1171 * regular files.
1172 *
1173 * The File class uses IPRT File API for file operations.
1174 */
1175class VBOXSETTINGS_CLASS File : public Input, public Output
1176{
1177public:
1178
1179 /**
1180 * Possible file access modes.
1181 */
1182 enum Mode { Read, Write, ReadWrite };
1183
1184 /**
1185 * Opens a file with the given name in the given mode. If @a aMode is Read
1186 * or ReadWrite, the file must exist. If @a aMode is Write, the file must
1187 * not exist. Otherwise, an EIPRTFailure excetion will be thrown.
1188 *
1189 * @param aMode File mode.
1190 * @param aFileName File name.
1191 */
1192 File (Mode aMode, const char *aFileName);
1193
1194 /**
1195 * Uses the given file handle to perform file operations. The given file
1196 * handle must be already open and the @a aMode argument must match the
1197 * actual mode of the file handle (otherwise unexpected errors will
1198 * occur).
1199 *
1200 * The read/write position of the given handle will be reset to the
1201 * beginning of the file on success.
1202 *
1203 * Note that the given file handle will not be automatically closed upon
1204 * this object destruction.
1205 *
1206 * @param aHandle Open file handle.
1207 * @param aMode File mode of the open file handle.
1208 * @param aFileName File name (for reference).
1209 */
1210 File (Mode aMode, RTFILE aHandle, const char *aFileName = NULL);
1211
1212 /**
1213 * Destrroys the File object. If the object was created from a file name
1214 * the corresponding file will be automatically closed. If the object was
1215 * created from a file handle, it will remain open.
1216 */
1217 virtual ~File();
1218
1219 const char *uri() const;
1220
1221 uint64_t pos() const;
1222 void setPos (uint64_t aPos);
1223
1224 /**
1225 * See Input::read(). If this method is called in wrong file mode,
1226 * LogicError will be thrown.
1227 */
1228 int read (char *aBuf, int aLen);
1229
1230 /**
1231 * See Output::write(). If this method is called in wrong file mode,
1232 * LogicError will be thrown.
1233 */
1234 int write (const char *aBuf, int aLen);
1235
1236 /**
1237 * See Output::truncate(). If this method is called in wrong file mode,
1238 * LogicError will be thrown.
1239 */
1240 void truncate();
1241
1242private:
1243
1244 /* Obscure class data */
1245 struct Data;
1246 std::auto_ptr <Data> m;
1247
1248 /* auto_ptr data doesn't have proper copy semantics */
1249 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (File)
1250};
1251
1252/**
1253 * The MemoryBuf class represents a stream implementation that reads from the
1254 * memory buffer.
1255 */
1256class VBOXSETTINGS_CLASS MemoryBuf : public Input
1257{
1258public:
1259
1260 MemoryBuf (const char *aBuf, size_t aLen, const char *aURI = NULL);
1261
1262 virtual ~MemoryBuf();
1263
1264 const char *uri() const;
1265
1266 int read (char *aBuf, int aLen);
1267 uint64_t pos() const;
1268 void setPos (uint64_t aPos);
1269
1270private:
1271
1272 /* Obscure class data */
1273 struct Data;
1274 std::auto_ptr <Data> m;
1275
1276 /* auto_ptr data doesn't have proper copy semantics */
1277 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (MemoryBuf)
1278};
1279
1280class XmlKeyBackend;
1281
1282/**
1283 * The XmlTreeBackend class uses XML markup to store settings trees.
1284 */
1285class VBOXSETTINGS_CLASS XmlTreeBackend : public TreeBackend
1286{
1287public:
1288
1289 /** Flags for TreeBackend::read(). */
1290 enum
1291 {
1292 /**
1293 * Sbstitute default values for missing attributes that have defaults
1294 * in the XML schema. Otherwise, stringValue() will return NULL for
1295 * such attributes.
1296 */
1297 Read_AddDefaults = RT_BIT (0),
1298 };
1299
1300 /**
1301 * The Error class represents errors that may happen when parsing or
1302 * validating the XML document representing the settings tree.
1303 */
1304 class VBOXSETTINGS_CLASS Error : public RuntimeError
1305 {
1306 public:
1307
1308 Error (const char *aMsg = NULL) : RuntimeError (aMsg) {}
1309 };
1310
1311 /**
1312 * The EConversionCycle class represents a conversion cycle detected by the
1313 * AutoConverter::needsConversion() implementation.
1314 */
1315 class VBOXSETTINGS_CLASS EConversionCycle : public Error
1316 {
1317 public:
1318
1319 EConversionCycle (const char *aMsg = NULL) : Error (aMsg) {}
1320 };
1321
1322 /**
1323 * The InputResolver class represents an interface to provide input streams
1324 * for external entities given an URL and entity ID.
1325 */
1326 class VBOXSETTINGS_CLASS InputResolver
1327 {
1328 public:
1329
1330 /**
1331 * Returns a newly allocated input stream for the given arguments. The
1332 * caller will delete the returned object when no more necessary.
1333 *
1334 * @param aURI URI of the external entity.
1335 * @param aID ID of the external entity (may be NULL).
1336 *
1337 * @return Input stream created using @c new or NULL to indicate
1338 * a wrong URI/ID pair.
1339 *
1340 * @todo Return by value after implementing the copy semantics for
1341 * Input subclasses.
1342 */
1343 virtual Input *resolveEntity (const char *aURI, const char *aID) = 0;
1344 };
1345
1346 /**
1347 * The AutoConverter class represents an interface to automatically convert
1348 * old settings trees to a new version when the tree is read from the
1349 * stream.
1350 */
1351 class VBOXSETTINGS_CLASS AutoConverter
1352 {
1353 public:
1354
1355 /**
1356 * Returns @true if the given tree needs to be converted using the XSLT
1357 * template identified by #templateUri(), or @false if no conversion is
1358 * required.
1359 *
1360 * The implementation normally checks for the "version" value of the
1361 * root key to determine if the conversion is necessary. When the
1362 * @a aOldVersion argument is not NULL, the implementation must return a
1363 * non-NULL non-empty string representing the old version (before
1364 * conversion) in it this string is used by XmlTreeBackend::oldVersion()
1365 * and must be non-NULL to indicate that the conversion has been
1366 * performed on the tree. The returned string must be allocated using
1367 * RTStrDup() or such.
1368 *
1369 * This method is called again after the successful transformation to
1370 * let the implementation retry the version check and request another
1371 * transformation if necessary. This may be used to perform multi-step
1372 * conversion like this: 1.1 => 1.2, 1.2 => 1.3 (instead of 1.1 => 1.3)
1373 * which saves from the need to update all previous conversion
1374 * templates to make each of them convert directly to the recent
1375 * version.
1376 *
1377 * @note Multi-step transformations are performed in a loop that exits
1378 * only when this method returns @false. It's up to the
1379 * implementation to detect cycling (repeated requests to convert
1380 * from the same version) wrong version order, etc. and throw an
1381 * EConversionCycle exception to break the loop without returning
1382 * @false (which means the transformation succeeded).
1383 *
1384 * @param aRoot Root settings key.
1385 * @param aOldVersionString Where to store old version string
1386 * pointer. May be NULL.
1387 */
1388 virtual bool needsConversion (const Key &aRoot,
1389 char **aOldVersion) const = 0;
1390
1391 /**
1392 * Returns the URI of the XSLT template to perform the conversion.
1393 * This template will be applied to the tree if #needsConversion()
1394 * returns @c true for this tree.
1395 */
1396 virtual const char *templateUri() const = 0;
1397 };
1398
1399 XmlTreeBackend();
1400 ~XmlTreeBackend();
1401
1402 /**
1403 * Sets an external entity resolver used to provide input streams for
1404 * entities referred to by the XML document being parsed.
1405 *
1406 * The given resolver object must exist as long as this instance exists or
1407 * until a different resolver is set using setInputResolver() or reset
1408 * using resetInputResolver().
1409 *
1410 * @param aResolver Resolver to use.
1411 */
1412 void setInputResolver (InputResolver &aResolver);
1413
1414 /**
1415 * Resets the entity resolver to the default resolver. The default
1416 * resolver provides support for 'file:' and 'http:' protocols.
1417 */
1418 void resetInputResolver();
1419
1420 /**
1421 * Sets a settings tree converter and enables the automatic conversion.
1422 *
1423 * The Automatic settings tree conversion is useful for upgrading old
1424 * settings files to the new version transparently during execution of the
1425 * #read() method.
1426 *
1427 * The automatic conversion takes place after reading the document from the
1428 * stream but before validating it. The given converter is asked if the
1429 * conversion is necessary using the AutoConverter::needsConversion() call,
1430 * and if so, the XSLT template specified by AutoConverter::templateUri() is
1431 * applied to the settings tree.
1432 *
1433 * Note that in order to make the result of the conversion permanent, the
1434 * settings tree needs to be exlicitly written back to the stream.
1435 *
1436 * The given converter object must exist as long as this instance exists or
1437 * until a different converter is set using setAutoConverter() or reset
1438 * using resetAutoConverter().
1439 *
1440 * @param aConverter Settings converter to use.
1441 */
1442 void setAutoConverter (AutoConverter &aConverter);
1443
1444 /**
1445 * Disables the automatic settings conversion previously enabled by
1446 * setAutoConverter(). By default automatic conversion it is disabled.
1447 */
1448 void resetAutoConverter();
1449
1450 /**
1451 * Returns a non-NULL string if the automatic settings conversion has been
1452 * performed during the last successful #read() call. Returns @c NULL if
1453 * there was no settings conversion.
1454 *
1455 * If #read() fails, this method will return the version string set by the
1456 * previous successful #read() call or @c NULL if there were no #read()
1457 * calls.
1458 */
1459 const char *oldVersion() const;
1460
1461 void rawRead (Input &aInput, const char *aSchema = NULL, int aFlags = 0);
1462 void rawWrite (Output &aOutput);
1463 void reset();
1464 Key &rootKey() const;
1465
1466private:
1467
1468 class XmlError;
1469
1470 /* Obscure class data */
1471 struct Data;
1472 std::auto_ptr <Data> m;
1473
1474 /* auto_ptr data doesn't have proper copy semantics */
1475 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (XmlTreeBackend)
1476
1477 static int ReadCallback (void *aCtxt, char *aBuf, int aLen);
1478 static int WriteCallback (void *aCtxt, const char *aBuf, int aLen);
1479 static int CloseCallback (void *aCtxt);
1480
1481 static void ValidityErrorCallback (void *aCtxt, const char *aMsg, ...);
1482 static void ValidityWarningCallback (void *aCtxt, const char *aMsg, ...);
1483 static void StructuredErrorCallback (void *aCtxt, xmlErrorPtr aErr);
1484
1485 static xmlParserInput *ExternalEntityLoader (const char *aURI,
1486 const char *aID,
1487 xmlParserCtxt *aCtxt);
1488
1489 static XmlTreeBackend *sThat;
1490
1491 static XmlKeyBackend *GetKeyBackend (const Key &aKey)
1492 { return (XmlKeyBackend *) TreeBackend::GetKeyBackend (aKey); }
1493};
1494
1495} /* namespace settings */
1496
1497#if defined(_MSC_VER)
1498#pragma warning (default:4251)
1499#endif
1500
1501#endif /* IN_RING3 */
1502
1503/** @} */
1504
1505#endif /* ___VBox_settings_h */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use