VirtualBox

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

Last change on this file since 14875 was 14854, checked in by vboxsync, 16 years ago

Main: rip XML classes out of settings code and move them to independent files and new vboxxml namespace to make them useful outside of the settings context

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