VirtualBox

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

Last change on this file since 6561 was 6310, checked in by vboxsync, 17 years ago

Fix formatting bugs in the XML config file code. The number base was
lost, and also signed values would have been stored as unsigned.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 43.0 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 * Currently, the only difference from #read() is that this method doesn't
1112 * set the stream position to the beginnign before and after reading but
1113 * instead leaves it as is in both cases which can give unexpected
1114 * results.
1115 *
1116 * @see read()
1117 */
1118 virtual void rawRead (Input &aInput, const char *aSchema = NULL,
1119 int aFlags = 0) = 0;
1120
1121 /**
1122 * Writes the current settings tree to the given output stream.
1123 *
1124 * This method will set the read/write position to the beginning of the
1125 * given stream before writing. After the settings have been successfully
1126 * written to the stream, the stream will be truncated at the position
1127 * following the last byte written by this method anc ghd position will be
1128 * set back to the beginning.
1129 *
1130 * @param aOutput Output stream.
1131 */
1132 void write (Output &aOutput)
1133 {
1134 aOutput.setPos (0);
1135 rawWrite (aOutput);
1136 aOutput.truncate();
1137 aOutput.setPos (0);
1138 }
1139
1140 /**
1141 * Writes the current settings tree to the given output stream in a raw
1142 * fashion.
1143 *
1144 * Currently, the only difference from #write() is that this method
1145 * doesn't set the stream position to the beginning before and after
1146 * reading and doesn't thruncate the stream, but instead leaves it as is
1147 * in both cases which can give unexpected results.
1148 *
1149 * @see write()
1150 */
1151 virtual void rawWrite (Output &aOutput) = 0;
1152
1153 /**
1154 * Deletes the current settings tree.
1155 */
1156 virtual void reset() = 0;
1157
1158 /**
1159 * Returns the root settings key.
1160 */
1161 virtual Key &rootKey() const = 0;
1162
1163protected:
1164
1165 static Key::Backend *GetKeyBackend (const Key &aKey) { return aKey.m.raw(); }
1166};
1167
1168//////////////////////////////////////////////////////////////////////////////
1169
1170/**
1171 * The File class is a stream implementation that reads from and writes to
1172 * regular files.
1173 *
1174 * The File class uses IPRT File API for file operations.
1175 */
1176class VBOXSETTINGS_CLASS File : public Input, public Output
1177{
1178public:
1179
1180 /**
1181 * Possible file access modes.
1182 */
1183 enum Mode { Read, Write, ReadWrite };
1184
1185 /**
1186 * Opens a file with the given name in the given mode. If @a aMode is Read
1187 * or ReadWrite, the file must exist. If @a aMode is Write, the file must
1188 * not exist. Otherwise, an EIPRTFailure excetion will be thrown.
1189 *
1190 * @param aMode File mode.
1191 * @param aFileName File name.
1192 */
1193 File (Mode aMode, const char *aFileName);
1194
1195 /**
1196 * Uses the given file handle to perform file operations. The given file
1197 * handle must be already open and the @a aMode argument must match the
1198 * actual mode of the file handle (otherwise unexpected errors will
1199 * occur).
1200 *
1201 * The read/write position of the given handle will be reset to the
1202 * beginning of the file on success.
1203 *
1204 * Note that the given file handle will not be automatically closed upon
1205 * this object destruction.
1206 *
1207 * @param aHandle Open file handle.
1208 * @param aMode File mode of the open file handle.
1209 * @param aFileName File name (for reference).
1210 */
1211 File (Mode aMode, RTFILE aHandle, const char *aFileName = NULL);
1212
1213 /**
1214 * Destrroys the File object. If the object was created from a file name
1215 * the corresponding file will be automatically closed. If the object was
1216 * created from a file handle, it will remain open.
1217 */
1218 virtual ~File();
1219
1220 const char *uri() const;
1221
1222 uint64_t pos() const;
1223 void setPos (uint64_t aPos);
1224
1225 /**
1226 * See Input::read(). If this method is called in wrong file mode,
1227 * LogicError will be thrown.
1228 */
1229 int read (char *aBuf, int aLen);
1230
1231 /**
1232 * See Output::write(). If this method is called in wrong file mode,
1233 * LogicError will be thrown.
1234 */
1235 int write (const char *aBuf, int aLen);
1236
1237 /**
1238 * See Output::truncate(). If this method is called in wrong file mode,
1239 * LogicError will be thrown.
1240 */
1241 void truncate();
1242
1243private:
1244
1245 /* Obscure class data */
1246 struct Data;
1247 std::auto_ptr <Data> m;
1248
1249 /* auto_ptr data doesn't have proper copy semantics */
1250 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (File)
1251};
1252
1253/**
1254 * The MemoryBuf class represents a stream implementation that reads from the
1255 * memory buffer.
1256 */
1257class VBOXSETTINGS_CLASS MemoryBuf : public Input
1258{
1259public:
1260
1261 MemoryBuf (const char *aBuf, size_t aLen, const char *aURI = NULL);
1262
1263 virtual ~MemoryBuf();
1264
1265 const char *uri() const;
1266
1267 int read (char *aBuf, int aLen);
1268 uint64_t pos() const;
1269 void setPos (uint64_t aPos);
1270
1271private:
1272
1273 /* Obscure class data */
1274 struct Data;
1275 std::auto_ptr <Data> m;
1276
1277 /* auto_ptr data doesn't have proper copy semantics */
1278 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (MemoryBuf)
1279};
1280
1281class XmlKeyBackend;
1282
1283/**
1284 * The XmlTreeBackend class uses XML markup to store settings trees.
1285 */
1286class VBOXSETTINGS_CLASS XmlTreeBackend : public TreeBackend
1287{
1288public:
1289
1290 /** Flags for TreeBackend::read(). */
1291 enum
1292 {
1293 /**
1294 * Sbstitute default values for missing attributes that have defaults
1295 * in the XML schema. Otherwise, stringValue() will return NULL for
1296 * such attributes.
1297 */
1298 Read_AddDefaults = RT_BIT (0),
1299 };
1300
1301 /**
1302 * The InputResolver class represents an input resolver, the service used to
1303 * provide input streams for external entities given an URL and entity ID.
1304 */
1305 class VBOXSETTINGS_CLASS InputResolver
1306 {
1307 public:
1308
1309 /**
1310 * Returns a newly allocated input stream for the given arguments. The
1311 * caller will delete the returned object when no more necessary.
1312 *
1313 * @param aURI URI of the external entity.
1314 * @param aID ID of the external entity (may be NULL).
1315 *
1316 * @return Input stream created using @c new or NULL to indicate
1317 * a wrong URI/ID pair.
1318 *
1319 * @todo Return by value after implementing the copy semantics for
1320 * Input subclasses.
1321 */
1322 virtual Input *resolveEntity (const char *aURI, const char *aID) = 0;
1323 };
1324
1325
1326 /**
1327 * The Error class represents errors that may happen when parsing or
1328 * validating the XML document representing the settings tree.
1329 */
1330 class VBOXSETTINGS_CLASS Error : public RuntimeError
1331 {
1332 public:
1333
1334 Error (const char *aMsg = NULL) : RuntimeError (aMsg) {}
1335 };
1336
1337 XmlTreeBackend();
1338 ~XmlTreeBackend();
1339
1340 /**
1341 * Sets an external entity resolver used to provide input streams for
1342 * entities referred to by the XML document being parsed.
1343 *
1344 * The given resolver object must exist as long as this instance exists or
1345 * until a different resolver is set using setInputResolver() or reset
1346 * using resetInputResolver().
1347 *
1348 * @param aResolver Resolver to use.
1349 */
1350 void setInputResolver (InputResolver &aResolver);
1351
1352 /**
1353 * Resets the entity resolver to the default resolver. The default
1354 * resolver provides support for 'file:' and 'http:' protocols.
1355 */
1356 void resetInputResolver();
1357
1358 void rawRead (Input &aInput, const char *aSchema = NULL, int aFlags = 0);
1359 void rawWrite (Output &aOutput);
1360 void reset();
1361 Key &rootKey() const;
1362
1363private:
1364
1365 class XmlError;
1366
1367 /* Obscure class data */
1368 struct Data;
1369 std::auto_ptr <Data> m;
1370
1371 /* auto_ptr data doesn't have proper copy semantics */
1372 DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP (XmlTreeBackend)
1373
1374 static int ReadCallback (void *aCtxt, char *aBuf, int aLen);
1375 static int WriteCallback (void *aCtxt, const char *aBuf, int aLen);
1376 static int CloseCallback (void *aCtxt);
1377
1378 static void ValidityErrorCallback (void *aCtxt, const char *aMsg, ...);
1379 static void ValidityWarningCallback (void *aCtxt, const char *aMsg, ...);
1380 static void StructuredErrorCallback (void *aCtxt, xmlErrorPtr aErr);
1381
1382 static xmlParserInput *ExternalEntityLoader (const char *aURI,
1383 const char *aID,
1384 xmlParserCtxt *aCtxt);
1385
1386 static XmlTreeBackend *sThat;
1387
1388 static XmlKeyBackend *GetKeyBackend (const Key &aKey)
1389 { return (XmlKeyBackend *) TreeBackend::GetKeyBackend (aKey); }
1390};
1391
1392} /* namespace settings */
1393
1394#if defined(_MSC_VER)
1395#pragma warning (default:4251)
1396#endif
1397
1398#endif /* IN_RING3 */
1399
1400/** @} */
1401
1402#endif /* ___VBox_settings_h */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette