#ifndef __TXMPMeta_hpp__
#define __TXMPMeta_hpp__    1

#if ( ! __XMP_hpp__ )
    #error "Do not directly include, use XMP.hpp"
#endif

#include "XMPCore/XMPCoreDefines.h"
#if ENABLE_CPP_DOM_MODEL
	#include "XMPCore/XMPCoreFwdDeclarations.h"
#endif

// =================================================================================================
// Copyright Adobe
// Copyright 2002 Adobe
// All Rights Reserved
//
// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it. 
// =================================================================================================

// =================================================================================================
/// \file TXMPMeta.hpp
/// \brief API for access to the XMP Toolkit core services.
///
/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It must be
/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for
/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp
/// for specific instantiation instructions. Please that you MUST NOT derive a class from this class,
/// consider this class FINAL, use it directly. [1279031]
///
/// Access these functions through the concrete class, \c SXMPMeta.
// =================================================================================================

// =================================================================================================
/// \class TXMPMeta TXMPMeta.hpp
/// \brief API for access to the XMP Toolkit core services.
///
/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It should be
/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for
/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp
/// for specific instantiation instructions.
///
/// Access these functions through the concrete class, \c SXMPMeta.
///
/// You can create \c TXMPMeta objects (also called XMP objects) from metadata that you construct,
/// or that you obtain from files using the XMP Toolkit's XMPFiles component; see \c TXMPFiles.hpp.
// =================================================================================================

template <class tStringObj> class TXMPIterator;
template <class tStringObj> class TXMPUtils;

// -------------------------------------------------------------------------------------------------

template <class tStringObj> class TXMPMeta {

public:

    // =============================================================================================
    // Initialization and termination
    // ==============================

    // ---------------------------------------------------------------------------------------------
    /// \name Initialization and termination
    ///
    /// @{

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetVersionInfo() retrieves runtime version information.
    ///
    /// The header \c XMPVersion.hpp defines a static version number for the XMP Toolkit, which
    /// describes the version of the API used at client compile time. It is not necessarily the same
    /// as the runtime version. Do not base runtime decisions on the static version alone; you can,
    /// however, compare the runtime and static versions.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta). The
    /// function can be called before calling \c TXMPMeta::Initialize().
    ///
    /// @param info [out] A buffer in which to return the version information.

    static void GetVersionInfo ( XMP_VersionInfo * info );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c Initialize() explicitly initializes the XMP Toolkit before use. */

    /// Initializes the XMP Toolkit.
    ///
    /// Call this function before making any other calls to the \c TXMPMeta functions, except
    /// \c TXMPMeta::GetVersionInfo().
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
    ///
    /// @return True on success. */
    static bool Initialize();
    // ---------------------------------------------------------------------------------------------
    /// @brief \c Terminate() explicitly terminates usage of the XMP Toolkit.
    ///
    /// Frees structures created on initialization.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).

    static void Terminate();

    /// @}

    // =============================================================================================
    // Constuctors and destructor
    // ==========================

    // ---------------------------------------------------------------------------------------------
    /// \name Constructors and destructor
    /// @{

    // ---------------------------------------------------------------------------------------------
    /// @brief Default constructor, creates an empty object.
    ///
    /// The default constructor creates a new empty \c TXMPMeta object.
    TXMPMeta();

    // ---------------------------------------------------------------------------------------------
    /// @brief Copy constructor, creates a client object refering to the same internal object.
    ///
    /// The copy constructor creates a new \c TXMPMeta object that refers to the same internal XMP
    /// object. as an existing \c TXMPMeta object.
    ///
    /// @param original The object to copy. new object is returned via Original only.
    ///

    TXMPMeta ( const TXMPMeta<tStringObj> & original );

    // ---------------------------------------------------------------------------------------------
    /// @brief Assignment operator, assigns the internal reference and increments the reference count.
    ///
    /// The assignment operator assigns the internal ref from the rhs object and increments the
    /// reference count on the underlying internal XMP object.

    void operator= ( const TXMPMeta<tStringObj> & rhs );

    // ---------------------------------------------------------------------------------------------
    /// @brief Reconstructs an XMP object from an internal reference.
    ///
    /// This constructor creates a new \c TXMPMeta object that refers to the underlying reference object
    /// of an existing \c TXMPMeta object. Use to safely pass XMP objects across DLL boundaries.
    ///
    /// @param xmpRef The underlying reference object, obtained from some other XMP object with
    /// \c TXMPMeta::GetInternalRef().returns The new object.
    ///
    ///

    TXMPMeta ( XMPMetaRef xmpRef );

    // ---------------------------------------------------------------------------------------------
    /// @brief Constructs an object and parse one buffer of RDF into it.
    ///
    /// This constructor creates a new \c TXMPMeta object and populates it with metadata from a
    /// buffer containing serialized RDF. This buffer must be a complete RDF parse stream.
    ///
    /// The result of passing serialized data to this function is identical to creating an empty
    /// object then calling \c TXMPMeta::ParseFromBuffer(). To use the constructor, however, the RDF
    /// must be complete. If you need to parse data from multiple buffers, create an empty object
    /// and use  \c TXMPMeta::ParseFromBuffer().
    ///
    /// @param buffer  A pointer to the buffer of RDF to be parsed. Can be null if the length is 0;
    /// in this case, the function creates an empty object.
    ///
    /// @param xmpSize  The length in bytes of the buffer.
    ///
    

    TXMPMeta ( XMP_StringPtr buffer,
               XMP_StringLen xmpSize );

    // ---------------------------------------------------------------------------------------------
    /// @brief Destructor, typical virtual destructor. */
    virtual ~TXMPMeta() throw();

    /// @}

    // =============================================================================================
    // Global state functions
    // ======================

    // ---------------------------------------------------------------------------------------------
    /// \name Global option flags
    /// @{
    /// Global option flags affect the overall behavior of the XMP Toolkit. The available options
    /// will be declared in \c XMP_Const.h. There are none in this version of the Toolkit.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetGlobalOptions() retrieves the set of global option flags. There are none in
    /// this version of the Toolkit.
    ///
    /// This function is static; you can make the call from the class without instantiating it.
    ///
    /// @return A logical OR of global option bit-flag constants.

     static XMP_OptionBits GetGlobalOptions();

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetGlobalOptions() updates the set of global option flags. There are none in this
    /// version of the Toolkit.
    ///
    /// The entire set is replaced with the new values. If only one flag is to be modified, use
    /// \c TXMPMeta::GetGlobalOptions() to obtain the current set, modify the desired flag, then use
    /// this function to reset the value.
    ///
    /// This function is static; you can make the call from the class without instantiating it.
    ///
    /// @param options A logical OR of global option bit-flag constants.

    static void SetGlobalOptions ( XMP_OptionBits options );

    /// @}

    // ---------------------------------------------------------------------------------------------
    /// \name Internal data structure dump utilities
    /// @{
    ///
    /// These are debugging utilities that dump internal data structures, to be handled by
    /// client-defined callback described in \c XMP_Const.h.
	///
	/// @see Member function \c TXMPMeta::DumpObject()

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DumpNamespaces() sends the list of registered namespace URIs and prefixes to a handler.
    ///
    /// For debugging. Invokes a client-defined callback for each line of output.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
    ///
    /// @param outProc The client-defined procedure to handle each line of output.
    ///
    /// @param clientData A pointer to client-defined data to pass to the handler.
    ///
    /// @return	A success-fail status value, returned from the handler. Zero is success, failure
    /// values are client-defined.

    static XMP_Status DumpNamespaces ( XMP_TextOutputProc outProc,
                     				   void *             clientData );

    /// @}

    // ---------------------------------------------------------------------------------------------
    /// \name Namespace Functions
    /// @{
    ///
    /// Namespaces must be registered before use in namespace URI parameters or path expressions.
    /// Within the XMP Toolkit the registered namespace URIs and prefixes must be unique. Additional
    /// namespaces encountered when parsing RDF are automatically registered.
    ///
    /// The namespace URI should always end in an XML name separator such as '/' or '#'. This is
    /// because some forms of RDF shorthand catenate a namespace URI with an element name to form a
    /// new URI.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c RegisterNamespace() registers a namespace URI with a suggested prefix.
    ///
    /// If the URI is not registered but the suggested prefix is in use, a unique prefix is created
    /// from the suggested one. The actual registered prefix is returned. The function result tells
    /// if the registered prefix is the suggested one. It is not an error if the URI is already
    /// registered, regardless of the prefix.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
    ///
    /// @param namespaceURI The URI for the namespace. Must be a valid XML URI.
    ///
    /// @param suggestedPrefix The suggested prefix to be used if the URI is not yet registered.
    /// Must be a valid XML name.
    ///
    /// @param registeredPrefix [out] A string object in which to return the prefix actually
    /// registered for this URI.
    ///
    /// @return True if the registered prefix matches the suggested prefix.
    ///
    /// @note No checking is done on either the URI or the prefix.  */

    static bool RegisterNamespace ( XMP_StringPtr namespaceURI,
                        			XMP_StringPtr suggestedPrefix,
                       				tStringObj *  registeredPrefix );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetNamespacePrefix() obtains the prefix for a registered namespace URI, and
    /// reports whether the URI is registered.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
    ///
    /// @param namespaceURI The URI for the namespace. Must not be null or the empty string. It is
    /// not an error if the namespace URI is not registered.
    ///
    /// @param namespacePrefix [out] A string object in which to return the prefix registered for
    /// this URI, with a terminating colon character, ':'. If the namespace is not registered, this
    /// string is not modified.
    ///
    /// @return True if the namespace URI is registered.

    static bool GetNamespacePrefix ( XMP_StringPtr namespaceURI,
                         			 tStringObj *  namespacePrefix );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetNamespaceURI() obtains the URI for a registered namespace prefix, and reports
    /// whether the prefix is registered.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
    ///
    /// @param namespacePrefix The prefix for the namespace. Must not be null or the empty string.
    /// It is not an error if the namespace prefix is not registered.
    ///
    /// @param namespaceURI [out] A string object in which to return the URI registered for this
    /// prefix. If the prefix is not registered, this string is not modified.
    ///
    /// @return True if the namespace prefix is registered.

    static bool GetNamespaceURI ( XMP_StringPtr namespacePrefix,
                      			  tStringObj *  namespaceURI );

    // ---------------------------------------------------------------------------------------------
    /// @brief Not implemented.
    ///
    /// Deletes a namespace from the registry. Does nothing if the URI is not registered, or if the
    /// parameter is null or the empty string.
    ///
    /// This function is static; make the call directly from the concrete class (\c SXMPMeta).
    ///
    /// @param namespaceURI The URI for the namespace.

    static void DeleteNamespace ( XMP_StringPtr namespaceURI );

    /// @}

    // =============================================================================================
    // Basic property manipulation functions
    // =====================================

    // *** Should add discussion of schemaNS and propName prefix usage.

    // ---------------------------------------------------------------------------------------------
    /// \name Accessing property values
    /// @{
    ///
    /// The property value accessors all take a property specification; the top level namespace URI
    ///	(the "schema" namespace) and the basic name of the property being referenced. See the
    ///	introductory discussion of path expression usage for more information.
	///
    /// The accessor functions return true if the specified property exists. If it does, output
    /// parameters return the value (if any) and option flags describing the property. The option
    /// bit-flag constants that describe properties are \c kXMP_PropXx and
	/// \c kXMP_ArrayIsXx. See \c #kXMP_PropValueIsURI and following, and macros \c #XMP_PropIsSimple
	/// and following in \c XMP_Const.h. If the property exists and has a value, it is returned as a
	/// Unicode string in UTF-8 encoding. Arrays and the non-leaf levels of structs do not have
	/// values.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetProperty() reports whether a property exists, and retrieves its value.
    ///
    /// This is the simplest property accessor. Use this to retrieve the values of top-level simple
    /// properties, or after using the path composition functions in \c TXMPUtils.
    ///
    /// When specifying a namespace and path (in this and all other accessors):
    ///   \li If a namespace URI is specified, it must be for a registered namespace.
    ///   \li If the namespace is specified only by a prefix in the property name path,
    /// it must be a registered prefix.
    ///   \li If both a URI and path prefix are present, they must be corresponding
    /// parts of a registered namespace.
    ///
    /// @param schemaNS The namespace URI for the property. The URI must be for a registered
    /// namespace. Must not be null or the empty string.
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string. The first component can be a namespace prefix; if present without a
    /// \c schemaNS value, the prefix specifies the namespace. The prefix must be for a registered
    /// namespace, and if a namespace URI is specified, must match the registered prefix for that
    /// namespace.
    ///
    /// @param propValue [out] A string object in which to return the value of the property, if the
    /// property exists and has a value. Arrays and non-leaf levels of structs do not have values.
    /// Can be null if the value is not wanted.
    ///
    /// @param options A buffer in which to return option flags describing the property. Can be null
    /// if the flags are not wanted.
    ///
    /// @return True if the property exists.

    bool GetProperty ( XMP_StringPtr    schemaNS,
                 	   XMP_StringPtr    propName,
                       tStringObj *     propValue,
                  	   XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetArrayItem() provides access to items within an array.
    ///
    /// Reports whether the item exists; if it does, and if it has a value, the function retrieves
    /// the value. Items are accessed by an integer index, where the first item has index 1.
    ///
    /// @param schemaNS The namespace URI for the array; see \c GetProperty().
    ///
    /// @param arrayName The name of the array. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
    /// to specify the last existing array item.
    ///
    /// @param itemValue [out] A string object in which to return the value of the array item, if it
    /// has a value. Arrays and non-leaf levels of structs do not have values. Can be null if the
    /// value is not wanted.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the array item.
    /// Can be null if the flags are not wanted.
    ///
    /// @return True if the array item exists.

    bool GetArrayItem ( XMP_StringPtr    schemaNS,
                   		XMP_StringPtr    arrayName,
                   		XMP_Index        itemIndex,
                   		tStringObj *     itemValue,
                   		XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetStructField() provides access to fields within a nested structure.
    ///
    /// Reports whether the field exists; if it does, and if it has a value, the function retrieves
    /// the value.
    ///
    /// @param schemaNS The namespace URI for the struct; see \c GetProperty().
    ///
    /// @param structName The name of the struct. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the \c schemaNS
    /// and \c structName parameters.
    ///
    /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
    /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters.
    ///
    /// @param fieldValue [out] A string object in which to return the value of the field, if the
    /// field has a value. Arrays and non-leaf levels of structs do not have values. Can be null if
    /// the value is not wanted.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the field. Can
    /// be null if the flags are not wanted.
    ///
    /// @return True if the field exists.

    bool GetStructField ( XMP_StringPtr    schemaNS,
                     	  XMP_StringPtr    structName,
                     	  XMP_StringPtr    fieldNS,
                     	  XMP_StringPtr    fieldName,
                     	  tStringObj *     fieldValue,
                     	  XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetQualifier() provides access to a qualifier attached to a property.
    ///
    /// @note In this version of the Toolkit, qualifiers are supported only for simple leaf properties.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property to which the qualifier is attached. Can be a
    /// general path expression, must not be null or the empty string; see \c GetProperty() for
    /// namespace prefix usage.
    ///
    /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the
    /// \c schemaNS and \c propName parameters.
    ///
    /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
    /// the empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters.
    ///
    /// @param qualValue [out] A string object in which to return the value of the qualifier, if the
    /// qualifier has a value. Arrays and non-leaf levels of structs do not have values. Can be null
    /// if the value is not wanted.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the qualifier.
    /// Can be null if the flags are not wanted.
    ///
    /// @return True if the qualifier exists.

    bool GetQualifier ( XMP_StringPtr    schemaNS,
                   		XMP_StringPtr    propName,
                  		XMP_StringPtr    qualNS,
                  		XMP_StringPtr    qualName,
                   		tStringObj *     qualValue,
                   		XMP_OptionBits * options ) const;

    /// @}

    // =============================================================================================

    // ---------------------------------------------------------------------------------------------
    /// \name Creating properties and setting their values
 	/// @{
 	///
    /// These functions all take a property specification; the top level namespace URI (the "schema"
    /// namespace) and the basic name of the property being referenced. See the introductory
    /// discussion of path expression usage for more information.
	///
    /// All of the functions take a UTF-8 encoded Unicode string for the property value. Arrays and
    /// non-leaf levels of structs do not have values. The value can be passed as an
    /// \c #XMP_StringPtr (a pointer to a null-terminated string), or as a string object
    /// (\c tStringObj).

    /// Each function takes an options flag that describes the property. You can use these functions
    /// to create empty arrays and structs by setting appropriate option flags. When you assign a
    /// value, all levels of a struct that are implicit in the assignment are created if necessary.
    /// \c TXMPMeta::AppendArrayItem() implicitly creates the named array if necessary.
    ///
    /// The allowed option bit-flags include:
    ///   \li \c #kXMP_PropValueIsStruct - Can be used to create an empty struct.
    ///		A struct is implicitly created when the first field is set.
    ///   \li \c #kXMP_PropValueIsArray - By default, a general unordered array (bag).
    ///   \li \c #kXMP_PropArrayIsOrdered - An ordered array.
   	///   \li \c #kXMP_PropArrayIsAlternate - An alternative array.
   	///   \li \c #kXMP_PropArrayIsAltText - An alt-text array. Each array element must
    /// 	be a simple property with an \c xml:lang attribute.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty() creates or sets a property value.
    ///
    /// This is the simplest property setter. Use it for top-level simple properties, or after using
    /// the path composition functions in \c TXMPUtils.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue The new value, a pointer to a null terminated UTF-8 string. Must be null
    /// for arrays and non-leaf levels of structs that do not have values.
    ///
    /// @param options Option flags describing the property; a logical OR of allowed bit-flag
    /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
    /// that already exists.

    void SetProperty ( XMP_StringPtr  schemaNS,
					   XMP_StringPtr  propName,
					   XMP_StringPtr  propValue,
					   XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty() creates or sets a property value using a string object.
	///
    /// Overloads the basic form of the function, allowing you to pass a string object
	/// for the item value. It is otherwise identical; see details in the canonical form.

    void SetProperty ( XMP_StringPtr      schemaNS,
					   XMP_StringPtr      propName,
					   const tStringObj & propValue,
					   XMP_OptionBits     options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetArrayItem() creates or sets the value of an item within an array.
    ///
    /// Items are accessed by an integer index, where the first item has index 1. This function
    /// creates the item if necessary, but the array itself must already exist Use
    /// \c AppendArrayItem() to create arrays. A new item is automatically appended if the index is the
    /// array size plus 1. To insert a new item before or after an existing item, use option flags.
    ///
    /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param arrayName The name of the array. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
    /// to specify the last existing array item.
    ///
    /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a
    /// value.
    ///
    /// @param options Option flags describing the array type and insertion location for a new item;
    /// a logical OR of allowed bit-flag constants. The type, if specified, must match the existing
    /// array type, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or
    /// \c #kXMP_PropArrayIsAltText. Default (0 or \c #kXMP_NoOptions) matches the  existing array type.
    ///
    /// To insert a new item before or after the specified index, set flag \c #kXMP_InsertBeforeItem
    /// or \c #kXMP_InsertAfterItem.

    void SetArrayItem ( XMP_StringPtr  schemaNS,
					    XMP_StringPtr  arrayName,
					    XMP_Index      itemIndex,
					    XMP_StringPtr  itemValue,
					    XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetArrayItem() creates or sets the value of an item within an array using a string object.
    ///
    /// Overloads the basic form of the function, allowing you to pass a string object in which to
	/// return the item value. It is otherwise identical; see details in the canonical form.

    void SetArrayItem ( XMP_StringPtr      schemaNS,
					    XMP_StringPtr      arrayName,
					    XMP_Index          itemIndex,
					    const tStringObj & itemValue,
					    XMP_OptionBits     options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c AppendArrayItem() adds an item to an array, creating the array if necessary.
    ///
    /// This function simplifies construction of an array by not requiring that you pre-create an
    /// empty array. The array that is assigned is created automatically if it does not yet exist.
    /// If the array exists, it must have the form specified by the options. Each call appends a new
    /// item to the array.
    ///
    /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param arrayName The name of the array. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param arrayOptions Option flags describing the array type to create; a logical OR of
    /// allowed bit-flag constants, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or
    /// \c #kXMP_PropArrayIsAltText. If the array exists, must match the existing array type or be
    /// null (0 or \c #kXMP_NoOptions).
    ///
    /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a
    /// value.
    ///
    /// @param itemOptions Option flags describing the item type to create; one of the bit-flag
    /// constants \c #kXMP_PropValueIsArray or \c #kXMP_PropValueIsStruct to create a complex array
    /// item.

    void AppendArrayItem ( XMP_StringPtr  schemaNS,
						   XMP_StringPtr  arrayName,
						   XMP_OptionBits arrayOptions,
						   XMP_StringPtr  itemValue,
						   XMP_OptionBits itemOptions = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c AppendArrayItem() adds an item to an array using a string object value, creating
    /// the array if necessary.
    ///
    /// Overloads the basic form of the function, allowing you to pass a string object in which to
	/// return the item value. It is otherwise identical; see details in the canonical form.

    void AppendArrayItem ( XMP_StringPtr      schemaNS,
						   XMP_StringPtr      arrayName,
						   XMP_OptionBits     arrayOptions,
						   const tStringObj & itemValue,
						   XMP_OptionBits     itemOptions = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetStructField() creates or sets the value of a field within a nested structure.
    ///
    /// Use this to  set a value within an existing structure, create a new field within an existing
    /// structure, or create an empty structure of any depth. If you set a field in a structure that
    /// does not exist, the structure is automatically created.
    ///
    /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param structName The name of the struct. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as
    /// \c GetProperty().
    ///
    /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
    /// empty string. Same namespace and prefix usage as \c GetProperty().
    ///
    /// @param fieldValue The new value, a null-terminated UTF-8 string, if the field has a value.
    /// Null to create a new, empty struct or empty field in an existing struct.
    ///
    /// @param options Option flags describing the property, in which the bit-flag
    /// \c #kXMP_PropValueIsStruct must be set to create a struct.

    void SetStructField ( XMP_StringPtr   schemaNS,
						  XMP_StringPtr   structName,
						  XMP_StringPtr   fieldNS,
						  XMP_StringPtr   fieldName,
						  XMP_StringPtr   fieldValue,
						  XMP_OptionBits  options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetStructField() creates or sets the value of a field within a nested structure,
    /// using a string object.
    ///
    /// Overloads the basic form of the function, allowing you to pass a string object in which to
	/// return the field value. It is otherwise identical; see details in the canonical form.

    void SetStructField ( XMP_StringPtr      schemaNS,
						  XMP_StringPtr      structName,
						  XMP_StringPtr      fieldNS,
						  XMP_StringPtr      fieldName,
						  const tStringObj & fieldValue,
						  XMP_OptionBits     options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetQualifier() creates or sets a qualifier attached to a property.
    ///
    /// Use this to  set a value for an existing qualifier, or create a new qualifier. <<how do
    /// options work? macro vs bit-flag? interaction w/XMP_PropHasQualifier?>> Use
    /// \c TXMPUtils::ComposeQualifierPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property to which the qualifier is attached. Can be a
    /// general path expression, must not be null or the empty string; see \c GetProperty() for
    /// namespace prefix usage.
    ///
    /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as
    /// \c GetProperty().
    ///
    /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
    /// the empty string. Same namespace and prefix usage as \c GetProperty().
    ///
    /// @param qualValue The new value, a null-terminated UTF-8 string, if the qualifier has a
    /// value. Null to create a new, empty qualifier.
    ///
    /// @param options Option flags describing the <<qualified property? qualifier?>>, a logical OR
    /// of property-type bit-flag constants. Use the macro \c #XMP_PropIsQualifier to create a
    /// qualifier.	 <<??>>

    void SetQualifier ( XMP_StringPtr  schemaNS,
					    XMP_StringPtr  propName,
					    XMP_StringPtr  qualNS,
					    XMP_StringPtr  qualName,
					    XMP_StringPtr  qualValue,
					    XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetQualifier() creates or sets a qualifier attached to a property using a string object.
    ///
    /// Overloads the basic form of the function, allowing you to pass a string object
	/// for the qualifier value. It is otherwise identical; see details in the canonical form.

    void SetQualifier ( XMP_StringPtr      schemaNS,
					    XMP_StringPtr      propName,
					    XMP_StringPtr      qualNS,
					    XMP_StringPtr      qualName,
					    const tStringObj & qualValue,
					    XMP_OptionBits     options = 0 );

    /// @}

    // =============================================================================================

    // ---------------------------------------------------------------------------------------------
    /// \name Detecting and deleting properties.
    /// @{
    ///
    /// The namespace URI and prefix usage for property specifiers in these functions is the same as
    /// for \c TXMPMeta::GetProperty().

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DeleteProperty() deletes an XMP subtree rooted at a given property.
    ///
    /// It is not an error if the property does not exist.
    ///
    /// @param schemaNS The namespace URI for the property; see \c GetProperty().
    ///
    /// @param propName The name of the property; see \c GetProperty().

    void DeleteProperty ( XMP_StringPtr schemaNS,
                     	  XMP_StringPtr propName );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DeleteArrayItem() deletes an XMP subtree rooted at a given array item.
    ///
    /// It is not an error if the array item does not exist. Use
    /// \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI for the array; see \c GetProperty().
    ///
    /// @param arrayName The name of the array. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
    /// to specify the last existing array item.

    void DeleteArrayItem ( XMP_StringPtr schemaNS,
						   XMP_StringPtr arrayName,
						   XMP_Index     itemIndex );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DeleteStructField() deletes an XMP subtree rooted at a given struct field.
    ///
    /// It is not an error if the field does not exist.
    ///
    /// @param schemaNS The namespace URI for the struct; see \c GetProperty().
    ///
    /// @param structName The name of the struct. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as
    /// \c GetProperty().
    ///
    /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
    /// empty string. Same namespace and prefix usage as \c GetProperty().

    void DeleteStructField ( XMP_StringPtr schemaNS,
							 XMP_StringPtr structName,
							 XMP_StringPtr fieldNS,
							 XMP_StringPtr fieldName );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DeleteQualifier() deletes an XMP subtree rooted at a given qualifier.
    ///
    /// It is not an error if the qualifier does not exist.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property to which the qualifier is attached. Can be a
    /// general path expression, must not be null or the empty string; see \c GetProperty() for
    /// namespace prefix usage.
    ///
    /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as
    /// \c GetProperty().
    ///
    /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
    /// the empty string. Same namespace and prefix usage as \c GetProperty().

    void DeleteQualifier ( XMP_StringPtr schemaNS,
						   XMP_StringPtr propName,
						   XMP_StringPtr qualNS,
						   XMP_StringPtr qualName );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DoesPropertyExist() reports whether a property currently exists.
    ///
    /// @param schemaNS The namespace URI for the property; see \c GetProperty().
    ///
    /// @param propName The name of the property; see \c GetProperty().
    ///
    /// @return True if the property exists.

    bool DoesPropertyExist ( XMP_StringPtr schemaNS,
                        	 XMP_StringPtr propName ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DoesArrayItemExist() reports whether an array item currently exists.
    ///
    /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param arrayName The name of the array. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem
    /// to specify the last existing array item.
    ///
    /// @return True if the array item exists.

    bool DoesArrayItemExist ( XMP_StringPtr schemaNS,
							  XMP_StringPtr arrayName,
							  XMP_Index     itemIndex ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DoesStructFieldExist() reports whether a struct field currently exists.
    ///
    /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param structName The name of the struct. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as
    /// \c GetProperty().
    ///
    /// @param fieldName The name of the field. Must be a single XML name, must not be null or the
    /// empty string. Same namespace and prefix usage as \c GetProperty().
    ///
    /// @return True if the field exists.

    bool DoesStructFieldExist ( XMP_StringPtr schemaNS,
							    XMP_StringPtr structName,
							    XMP_StringPtr fieldNS,
							    XMP_StringPtr fieldName ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DoesQualifierExist() reports whether a qualifier currently exists.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property to which the qualifier is attached. Can be a
    /// general path expression, must not be null or the empty string; see \c GetProperty() for
    /// namespace prefix usage.
    ///
    /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as
    /// \c GetProperty().
    ///
    /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or
    /// the empty string. Same namespace and prefix usage as \c GetProperty().
    ///
    /// @return True if the qualifier exists.

    bool DoesQualifierExist ( XMP_StringPtr schemaNS,
							  XMP_StringPtr propName,
							  XMP_StringPtr qualNS,
							  XMP_StringPtr qualName ) const;

    /// @}

    // =============================================================================================
    // Specialized Get and Set functions
    // =============================================================================================

    // ---------------------------------------------------------------------------------------------
    /// \name Accessing properties as binary values.
    /// @{
    ///
	/// These are very similar to \c TXMPMeta::GetProperty() and \c TXMPMeta::SetProperty(), except
	/// that the value is returned or provided in binary form instead of as a UTF-8 string.
	/// \c TXMPUtils provides functions for converting between binary and string values.
    /// Use the path composition functions in  \c TXMPUtils	to compose complex path expressions
    /// for fields or items in nested structures or arrays, or for qualifiers.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetProperty_Bool() retrieves the value of a Boolean property as a C++ bool.
    ///
    /// Reports whether a property exists, and retrieves its binary value and property type information.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
    /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
    /// values.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the property, a
    /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
    /// be null if flags are not wanted.
    ///
    /// @return True if the property exists.

    bool GetProperty_Bool ( XMP_StringPtr    schemaNS,
						    XMP_StringPtr    propName,
						    bool *           propValue,
						    XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetProperty_Int() retrieves the value of an integer property as a C long integer.
    ///
    /// Reports whether a property exists, and retrieves its binary value and property type information.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
    /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
    /// values.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the property, a
    /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
    /// be null if flags are not wanted.
    ///
    /// @return True if the property exists.

    bool GetProperty_Int ( XMP_StringPtr    schemaNS,
						   XMP_StringPtr    propName,
						   XMP_Int32 *           propValue,
						   XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetProperty_Int64() retrieves the value of an integer property as a C long long integer.
    ///
    /// Reports whether a property exists, and retrieves its binary value and property type information.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
    /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
    /// values.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the property, a
    /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
    /// be null if flags are not wanted.
    ///
    /// @return True if the property exists.

    bool GetProperty_Int64 ( XMP_StringPtr    schemaNS,
							 XMP_StringPtr    propName,
							 XMP_Int64 *      propValue,
							 XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetProperty_Float() retrieves the value of a floating-point property as a C double float.
    ///
    /// Reports whether a property exists, and retrieves its binary value and property type information.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
    /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
    /// values.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the property, a
    /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
    /// be null if flags are not wanted.
    ///
    /// @return True if the property exists.

    bool GetProperty_Float ( XMP_StringPtr    schemaNS,
							 XMP_StringPtr    propName,
							 double *         propValue,
							 XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetProperty_Date() retrieves the value of a date-time property as an \c #XMP_DateTime structure.
    ///
    /// Reports whether a property exists, and retrieves its binary value and property type information.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue [out] A buffer in which to return the binary value. Can be null if the
    /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have
    /// values.
    ///
    /// @param options [out] A buffer in which to return the option flags describing the property, a
    /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can
    /// be null if flags are not wanted.
    ///
    /// @return True if the property exists.

    bool GetProperty_Date ( XMP_StringPtr    schemaNS,
						    XMP_StringPtr    propName,
						    XMP_DateTime *   propValue,
						    XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty_Bool() sets the value of a Boolean property using a C++ bool.
    ///
    /// Sets a property with a binary value, creating it if necessary.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue The new binary value. Can be null if creating the property. Must be null
    /// for arrays and non-leaf levels of structs that do not have values.
    ///
    /// @param options Option flags describing the property; a logical OR of allowed bit-flag
    /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
    /// that already exists.

    void SetProperty_Bool ( XMP_StringPtr  schemaNS,
						    XMP_StringPtr  propName,
						    bool           propValue,
						    XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty_Int() sets the value of an integer property using a C long integer.
    ///
    /// Sets a property with a binary value, creating it if necessary.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue The new binary value. Can be null if creating the property. Must be null
    /// for arrays and non-leaf levels of structs that do not have values.
    ///
    /// @param options Option flags describing the property; a logical OR of allowed bit-flag
    /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
    /// that already exists.

    void SetProperty_Int ( XMP_StringPtr  schemaNS,
						   XMP_StringPtr  propName,
						   XMP_Int32      propValue,
						   XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty_Int64() sets the value of an integer property using a C long long integer.
    ///
    /// Sets a property with a binary value, creating it if necessary.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue The new binary value. Can be null if creating the property. Must be null
    /// for arrays and non-leaf levels of structs that do not have values.
    ///
    /// @param options Option flags describing the property; a logical OR of allowed bit-flag
    /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
    /// that already exists.

    void SetProperty_Int64 ( XMP_StringPtr  schemaNS,
							 XMP_StringPtr  propName,
							 XMP_Int64     propValue,
							 XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty_Float() sets the value of a floating-point property using a C double float.
    ///
    /// Sets a property with a binary value, creating it if necessary.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue The new binary value. Can be null if creating the property. Must be null
    /// for arrays and non-leaf levels of structs that do not have values.
    ///
    /// @param options Option flags describing the property; a logical OR of allowed bit-flag
    /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
    /// that already exists.

    void SetProperty_Float ( XMP_StringPtr  schemaNS,
							 XMP_StringPtr  propName,
							 double         propValue,
							 XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetProperty_Date() sets the value of a date/time property using an \c #XMP_DateTime structure.
    ///
    /// Sets a property with a binary value, creating it if necessary.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param propName The name of the property. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param propValue The new binary value. Can be null if creating the property. Must be null
    /// for arrays and non-leaf levels of structs that do not have values.
    ///
    /// @param options Option flags describing the property; a logical OR of allowed bit-flag
    /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property
    /// that already exists.

    void SetProperty_Date ( XMP_StringPtr         schemaNS,
						    XMP_StringPtr         propName,
						    const XMP_DateTime &  propValue,
						    XMP_OptionBits        options = 0 );

    /// @}
    // =============================================================================================
    /// \name Accessing localized text (alt-text) properties.
    /// @{
    ///
	/// Localized text properties are stored in alt-text arrays. They allow multiple concurrent
	/// localizations of a property value, for example a document title or copyright in several
	/// languages.
    ///
    /// These functions provide convenient support for localized text properties, including a
    /// number of special and obscure aspects. The most important aspect of these functions is that
    /// they select an appropriate array item based on one or two RFC 3066 language tags. One of
    /// these languages, the "specific" language, is preferred and selected if there is an exact
    /// match. For many languages it is also possible to define a "generic" language that can be
    /// used if there is no specific language match. The generic language must be a valid RFC 3066
    /// primary subtag, or the empty string.
    ///
    /// For example, a specific language of "en-US" should be used in the US, and a specific
    /// language of "en-UK" should be used in England. It is also appropriate to use "en" as the
    /// generic language in each case. If a US document goes to England, the "en-US" title is
    /// selected by using the "en" generic language and the "en-UK" specific language.
    ///
    /// It is considered poor practice, but allowed, to pass a specific language that is just an
    /// RFC 3066 primary tag. For example "en" is not a good specific language, it should only be
    /// used as a generic language. Passing "i" or "x" as the generic language is also considered
    /// poor practice but allowed.
    ///
    /// Advice from the W3C about the use of RFC 3066 language tags can be found at:
    ///     \li http://www.w3.org/International/articles/language-tags/
    ///
    /// \note RFC 3066 language tags must be treated in a case insensitive manner. The XMP toolkit
    /// does this by normalizing their capitalization:
 	/// 	\li The primary subtag is lower case, the suggested practice of ISO 639.
	/// 	\li All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166.
	/// 	\li All other subtags are lower case.
    ///
    /// The XMP specification defines an artificial language, "x-default", that is used to
    /// explicitly denote a default item in an alt-text array. The XMP toolkit normalizes alt-text
    /// arrays such that the x-default item is the first item. The \c SetLocalizedText() function
    /// has several special features related to the x-default item, see its description for details.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetLocalizedText() retrieves information about a selected item in an alt-text array.
    ///
    /// The array item is selected according to these rules:
    ///   \li Look for an exact match with the specific language.
    ///   \li If a generic language is given, look for a partial match.
    ///   \li Look for an x-default item.
    ///   \li Choose the first item.
    ///
    /// A partial match with the generic language is where the start of the item's language matches
    /// the generic string and the next character is '-'. An exact match is also recognized as a
    /// degenerate case.
    ///
    /// You can pass "x-default" as the specific language. In this case, selection of an
    /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The
    /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a
    /// match.
    ///
    /// The return value reports whether a match was successfully made.
    ///
    /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty().
    ///
    /// @param altTextName The name of the alt-text array. Can be a general path expression, must
    /// not be null or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
    /// null or the empty string if no generic language is wanted.
    ///
    /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
    /// Must not be null or the empty string.
    ///
    /// @param actualLang [out] A string object in which to return the language of the selected
    /// array item, if an appropriate array item is found. Can be null if the language is not wanted.
    ///
    /// @param itemValue [out] A string object in which to return the value of the array item, if an
    /// appropriate array item is found. Can be null if the value is not wanted.
    ///
    /// @param options A buffer in which to return the option flags that describe the array item, if
    /// an appropriate array item is found. Can be null if the flags are not wanted.
    ///
    /// @return True if an appropriate array item exists.

    bool GetLocalizedText ( XMP_StringPtr    schemaNS,
						    XMP_StringPtr    altTextName,
						    XMP_StringPtr    genericLang,
						    XMP_StringPtr    specificLang,
						    tStringObj *     actualLang,
						    tStringObj *     itemValue,
						    XMP_OptionBits * options ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array.
    ///
    /// Creates an appropriate array item if necessary, and handles special cases for the x-default
    /// item.
    ///
    /// The array item is selected according to these rules:
    ///   \li Look for an exact match with the specific language.
    ///   \li If a generic language is given, look for a partial match.
    ///   \li Look for an x-default item.
    ///   \li Choose the first item.
    ///
    /// A partial match with the generic language is where the start of the item's language matches
    /// the generic string and the next character is '-'. An exact match is also recognized as a
    /// degenerate case.
    ///
    /// You can pass "x-default" as the specific language. In this case, selection of an
    /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The
    /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a
    /// match.
    ///
    /// Item values are modified according to these rules:
    ///
    ///   \li If the selected item is from a match with the specific language, the value of that
    ///   item is modified. If the existing value of that item matches the existing value of the
    ///   x-default item, the x-default item is also modified. If the array only has 1 existing item
    ///   (which is not x-default), an x-default item is added with the given value.
    ///
    ///   \li If the selected item is from a match with the generic language and there are no other
    ///   generic matches, the value of that item is modified. If the existing value of that item
    ///   matches the existing value of the x-default item, the x-default item is also modified. If
    ///   the array only has 1 existing item (which is not x-default), an x-default item is added
    ///   with the given value.
    ///
    ///   \li If the selected item is from a partial match with the generic language and there are
    ///   other partial matches, a new item is created for the specific language. The x-default item
    ///   is not modified.
    ///
    ///   \li If the selected item is from the last 2 rules then a new item is created for the
    ///   specific language. If the array only had an x-default item, the x-default item is also
    ///   modified. If the array was empty, items are created for the specific language and
    ///   x-default.
    ///
    /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty().
    ///
    /// @param altTextName The name of the alt-text array. Can be a general path expression, must
    /// not be null or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
    /// null or the empty string if no generic language is wanted.
    ///
    /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
    /// Must not be null or the empty string.
    ///
    /// @param itemValue The new value for the matching array item, specified as a null-terminated
    /// UTF-8 string.
    ///
    /// @param options Option flags, none currently defined.

    void SetLocalizedText ( XMP_StringPtr  schemaNS,
						    XMP_StringPtr  altTextName,
						    XMP_StringPtr  genericLang,
						    XMP_StringPtr  specificLang,
						    XMP_StringPtr  itemValue,
						    XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array using
    /// a string object.
    ///
    /// Creates an appropriate array item if necessary, and handles special cases for the x-default
    /// item.
    ///
    /// The array item is selected according to these rules:
    ///   \li Look for an exact match with the specific language.
    ///   \li If a generic language is given, look for a partial match.
    ///   \li Look for an x-default item.
    ///   \li Choose the first item.
    ///
    /// A partial match with the generic language is where the start of the item's language matches
    /// the generic string and the next character is '-'. An exact match is also recognized as a
    /// degenerate case.
    ///
    /// You can pass "x-default" as the specific language. In this case, selection of an \c x-default
    /// item is an exact match by the first rule, not a selection by the 3rd rule. The last 2 rules
    /// are fallbacks used when the specific and generic languages fail to produce a match.
    ///
    /// Item values are modified according to these rules:
    ///
    ///   \li If the selected item is from a match with the specific language, the value of that
    ///   item is modified. If the existing value of that item matches the existing value of the
    ///   x-default item, the x-default item is also modified. If the array only has 1 existing item
    ///   (which is not x-default), an x-default item is added with the given value.
    ///
    ///   \li If the selected item is from a match with the generic language and there are no other
    ///   generic matches, the value of that item is modified. If the existing value of that item
    ///   matches the existing value of the x-default item, the x-default item is also modified. If
    ///   the array only has 1 existing item (which is not x-default), an x-default item is added
    ///   with the given value.
    ///
    ///   \li If the selected item is from a partial match with the generic language and there are
    ///   other partial matches, a new item is created for the specific language. The x-default item
    ///   is not modified.
    ///
    ///   \li If the selected item is from the last 2 rules then a new item is created for the
    ///   specific language. If the array only had an x-default item, the x-default item is also
    ///   modified. If the array was empty, items are created for the specific language and
    ///   x-default.
    ///
    /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty().
    ///
    /// @param altTextName The name of the alt-text array. Can be a general path expression, must
    /// not be null or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
    /// null or the empty string if no generic language is wanted.
    ///
    /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
    /// Must not be null or the empty string.
    ///
    /// @param itemValue The new value for the matching array item, specified as a string object.
    ///
    /// @param options Option flags, none currently defined.

    void SetLocalizedText ( XMP_StringPtr      schemaNS,
						    XMP_StringPtr      altTextName,
						    XMP_StringPtr      genericLang,
						    XMP_StringPtr      specificLang,
						    const tStringObj & itemValue,
						    XMP_OptionBits     options = 0 );
	
    // ---------------------------------------------------------------------------------------------
	/// @brief \c DeleteLocalizedText() deletes specific language alternatives from an alt-text array.
	///
	///	The rules for finding the language value to delete are similar to those for \c #SetLocalizedText().
	///
	/// @param schemaNS The namespace URI for the alt-text array; see \c #GetProperty().
	///
	/// @param altTextName The name of the alt-text array. Can be a general path expression, must
	/// not be null or the empty string; see \c #GetProperty() for namespace prefix usage.
	///
	/// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be
	/// null or the empty string if no generic language is wanted.
	///
	/// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default".
	/// Must not be null or the empty string.
    ///
	void
	DeleteLocalizedText ( XMP_StringPtr    schemaNS,
							XMP_StringPtr    altTextName,
							XMP_StringPtr    genericLang,
							XMP_StringPtr    specificLang );

	/// @}

   	// =============================================================================================
    /// \name Creating and reading serialized RDF.
    /// @{
    ///
	/// The metadata contained in an XMP object must be serialized as RDF for storage in an XMP
	/// packet and output to a file. Similarly, metadata in the form of serialized RDF (such as
	/// metadata read from a file using \c TXMPFiles) must be parsed into an XMP object for
	/// manipulation with the XMP Toolkit.
	///
	/// These functions support parsing serialized RDF into an XMP object, and serializing an XMP
    /// object into RDF. The input for parsing can be any valid Unicode encoding. ISO Latin-1 is
    /// also recognized, but its use is strongly discouraged. Serialization is always as UTF-8.

    // ---------------------------------------------------------------------------------------------
    /// @brief \c ParseFromBuffer() parses RDF from a series of input buffers into this XMP object.
    ///
    /// Use this to convert metadata from serialized RDF form (as, for example, read from an XMP
    /// packet embedded in a file) into an XMP object that you can manipulate with the XMP Toolkit.
    /// If this XMP object is empty and the input buffer contains a complete XMP packet, this is the
    /// same as creating a new XMP object from that buffer with the constructor.
    ///
    /// You can use this function to combine multiple buffers into a single metadata tree. To
    /// terminate an input loop conveniently, pass  the option \c #kXMP_ParseMoreBuffers for all
    /// real input, then make a final call with a zero length and \c #kXMP_NoOptions. The buffers
    /// can be any length. The buffer boundaries need not respect XML tokens or even Unicode
    /// characters.
    ///
    /// @param buffer A pointer to a buffer of input. Can be null if \c bufferSize is 0.
    ///
    /// @param bufferSize The length of the input buffer in bytes. Zero is a valid value.
    ///
    /// @param options An options flag that controls how the parse operation is performed. A logical
    /// OR of these bit-flag constants:
    ///   \li \c #kXMP_ParseMoreBuffers - This is not the last buffer of input, more calls follow.
    ///   \li \c #kXMP_RequireXMPMeta - The \c x:xmpmeta XML element is required around \c rdf:RDF.
    ///
    /// @see \c TXMPFiles::GetXMP()

    void ParseFromBuffer ( XMP_StringPtr  buffer,
						   XMP_StringLen  bufferSize,
						   XMP_OptionBits options = 0 );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF.
    ///
    /// Use this to prepare metadata for storage as an XMP packet embedded in a file. See \c TXMPFiles::PutXMP().
    ///
    /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null.
    ///
    /// @param options An options flag that controls how the serialization operation is performed.
    /// The specified options must be logically consistent; an exception is thrown if they are not.
    /// A logical OR of these bit-flag constants:
    ///   \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be
    ///   specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or
    ///   \c #kXMP_ExactPacketLength.
    ///   \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified
    ///   together with \c kXMP_OmitPacketWrapper.
    ///   \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout.
    ///   \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the
    ///   padding if no \c xmp:Thumbnails property is present. Cannot be specified together with
    ///   \c kXMP_OmitPacketWrapper.
    ///   \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length.
    ///   The actual amount of padding is computed. An exception is thrown if the packet exceeds
    ///   this length with no padding.	Cannot be specified together with
    ///   \c kXMP_OmitPacketWrapper.
    ///
    /// In addition to the above options, you can include one of the following encoding options:
    ///   \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default.
    ///   \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16.
    ///   \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16.
    ///   \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32.
    ///   \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32.
    ///
    /// @param padding The amount of padding to be added if a writeable XML packet is created. If
    /// zero (the default) an appropriate amount of padding is computed.
    ///
    /// @param newline The string to be used as a line terminator. If empty, defaults to linefeed,
    /// U+000A, the standard XML newline.
    ///
    /// @param indent The string to be used for each level of indentation in the serialized RDF. If
    /// empty, defaults to two ASCII spaces, U+0020.
    ///
    /// @param baseIndent The number of levels of indentation to be used for the outermost XML
    /// element in the serialized RDF. This is convenient when embedding the RDF in other text.

    void SerializeToBuffer ( tStringObj *   rdfString,
							 XMP_OptionBits options,
							 XMP_StringLen  padding,
							 XMP_StringPtr  newline,
							 XMP_StringPtr  indent = "",
							 XMP_Index      baseIndent = 0 ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF.
    ///
    /// This simpler form of the function uses default values for the \c newline, \c indent, and
    /// \c baseIndent parameters.
    ///
    /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null.
    ///
    /// @param options An options flag that controls how the serialization operation is performed.
    /// The specified options must be logically consistent; an exception is thrown if they are not.
    /// A logical OR of these bit-flag constants:
    ///   \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be
    ///   specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or
    ///   \c #kXMP_ExactPacketLength.
    ///   \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified
    ///   together with \c kXMP_OmitPacketWrapper.
    ///   \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout.
    ///   \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the
    ///   padding if no \c xmp:Thumbnails property is present. Cannot be specified together with
    ///   \c kXMP_OmitPacketWrapper.
    ///   \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length.
    ///   The actual amount of padding is computed. An exception is thrown if the packet exceeds
    ///   this length with no padding.	Cannot be specified together with
    ///   \c kXMP_OmitPacketWrapper.
    ///
    /// In addition to the above options, you can include one of the following encoding options:
    ///   \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default.
    ///   \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16.
    ///   \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16.
    ///   \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32.
    ///   \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32.
    ///
    /// @param padding The amount of padding to be added if a writeable XML packet is created.
    /// If zero (the default) an appropriate amount of padding is computed.

    void SerializeToBuffer ( tStringObj *   rdfString,
							 XMP_OptionBits options = 0,
							 XMP_StringLen  padding = 0 ) const;

    /// @}
    // =============================================================================================
    // Miscellaneous Member Functions
    // ==============================

    // ---------------------------------------------------------------------------------------------
    /// \name Helper functions.
    /// @{

    // ---------------------------------------------------------------------------------------------
    /// @brief Retrieves an internal reference that can be safely passed across DLL boundaries and
    /// reconstructed.
    ///
    /// The \c TXMPMeta class is a normal C++ template, it is instantiated and local to each client
    /// executable, as are the other \c TXMP* classes. Different clients might not use the same
    /// string type to instantiate \c TXMPMeta.
    ///
    /// Because of this you should not pass \c SXMPMeta objects, or pointers to \c SXMPMeta objects,
    /// across DLL boundaries. Use this function to obtain a safe internal reference that you can
    /// pass, then construct a local object on the callee side. This construction does not create a
    /// cloned XMP tree, it is the same underlying XMP object safely wrapped in each client's
    /// \c SXMPMeta object.
    ///
    /// Use this function and the associated constructor like this:
    ///   \li The callee's header contains:
    /// <pre>
    /// CalleeMethod ( XMPMetaRef xmpRef );
    /// </pre>
    ///
    ///   \li The caller's code contains:
    /// <pre>
    /// SXMPMeta callerXMP;
    /// CalleeMethod ( callerXMP.GetInternalRef() );
    /// </pre>
    ///
    ///   \li The callee's code contains:
    /// <pre>
    /// SXMPMeta calleeXMP ( xmpRef );
    /// </pre>
    ///
    /// @return The reference object.

    XMPMetaRef GetInternalRef() const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c GetObjectName() retrieves the client-assigned name of this XMP object.
    ///
    /// Assign this name with \c SetObjectName().
    ///
    /// @param name [out] A string object in which to return the name.

    void GetObjectName ( tStringObj * name ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetObjectName() assigns a name to this XMP object.
    ///
    /// Retrieve this client-assigned name with \c GetObjectName().
    ///
    /// @param name The name as a null-terminated UTF-8 string.

    void SetObjectName ( XMP_StringPtr name );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c SetObjectName() assigns a name to this XMP object.
    ///
    /// Retrieve this client-assigned name with \c GetObjectName().
    ///
    /// @param name The name as a string object.

    void SetObjectName ( tStringObj name );

    // ---------------------------------------------------------------------------------------------
    /// @brief \c Sort() sorts the data model tree of an XMP object.
    ///
    /// Use this function to sort the data model of an XMP object into a canonical order. This can
    /// be convenient when comparing data models, (e.g. by text comparison of DumpObject output).
    ///
    /// At the top level the namespaces are sorted by their prefixes. Within a namespace, the top
    /// level properties are sorted by name. Within a struct, the fields are sorted by their
    /// qualified name, i.e. their XML prefix:local form. Unordered arrays of simple items are
    /// sorted by value. Language Alternative arrays are sorted by the xml:lang qualifiers, with
    /// the "x-default" item placed first.
    
    void Sort();

    // ---------------------------------------------------------------------------------------------
    /// @brief \c Erase() restores the object to a "just constructed" state.
    
    void Erase();
    
    // ---------------------------------------------------------------------------------------------
    /// @brief \c Clone() creates a deep copy of an XMP object.
    ///
    /// Use this function to copy an entire XMP metadata tree. Assignment and copy constructors only
    /// increment a reference count, they do not do a deep copy. This function returns an object,
    /// not a pointer. The following shows correct usage:
    ///
    /// <pre>
    /// SXMPMeta * clone1 = new SXMPMeta ( sourceXMP.Clone() );  // This works.
    /// SXMPMeta   clone2 ( sourceXMP.Clone );  	// This works also. (Not a pointer.)
    /// </pre>
    /// The \c clone2 example does not use an explicit pointer.
    /// This is good for local usage, protecting against memory leaks.
    ///
    /// This is an example of incorrect usage:
    /// <pre>
    /// SXMPMeta * clone3 = &sourceXMP.Clone();		// ! This does not work!
    /// </pre>
    /// The assignment to \c clone3 creates a temporary object, initializes it with the clone,
    /// assigns the address of the temporary to \c clone3, then deletes the temporary.
    ///
    /// @param options Option flags, not currently defined..
    ///
    /// @return An XMP object cloned from the original.

    TXMPMeta Clone ( XMP_OptionBits options = 0 ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c CountArrayItems() reports the number of items currently defined in an array.
    ///
    /// @param schemaNS The namespace URI; see \c GetProperty().
    ///
    /// @param arrayName The name of the array. Can be a general path expression, must not be null
    /// or the empty string; see \c GetProperty() for namespace prefix usage.
    ///
    /// @return The number of items.

    XMP_Index CountArrayItems ( XMP_StringPtr schemaNS,
                      			XMP_StringPtr arrayName ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief \c DumpObject() outputs the content of an XMP object to a callback handler for debugging.
    ///
    /// Invokes a client-defined callback for each line of output.
    ///
    /// @param outProc The client-defined procedure to handle each line of output.
    ///
    /// @param clientData A pointer to client-defined data to pass to the handler.
    ///
    /// @return	A success-fail status value, returned from the handler. Zero is success, failure
    /// values are client-defined.
    ///
    /// @see Static function \c DumpNamespaces()

    XMP_Status DumpObject ( XMP_TextOutputProc outProc,
                 			void *	           clientData ) const;

    // ---------------------------------------------------------------------------------------------
    /// @brief Not implemented
    XMP_OptionBits GetObjectOptions() const;

    // ---------------------------------------------------------------------------------------------
    /// \brief Not implemented
    void SetObjectOptions ( XMP_OptionBits options );

    /// @}

    // =============================================================================================
    // Error notifications
    // ===================

    // ---------------------------------------------------------------------------------------------
    /// \name Error notifications
    /// @{
	///
	/// From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error
	/// exception. For the most part exceptions were thrown early and thus API calls aborted as soon
	/// as an error was detected. Starting in version 5.5, support has been added for notifications
	/// of errors arising in calls to \c TXMPMeta functions.
	///
	/// A client can register an error notification callback function for a \c TXMPMeta object. This
	/// can be done as a global default or individually to each object. The global default applies
	/// to all objects created after it is registered. Within the object there is no difference
	/// between the global default or explicitly registered callback. The callback function returns
	/// a \c bool value indicating if recovery should be attempted (true) or an exception thrown
	/// (false). If no callback is registered, a best effort at recovery and continuation will be
	/// made with an exception thrown if recovery is not possible.
	///
	/// The number of notifications delivered for a given TXMPMeta object can be limited. This is
	/// intended to reduce chatter from multiple or cascading errors. The limit is set when the
	/// callback function is registered. This limits the number of notifications of the highest
	/// severity delivered or less. If a higher severity error occurs, the counting starts again.
	/// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit.
	
	//  --------------------------------------------------------------------------------------------
	/// @brief SetDefaultErrorCallback() registers a global default error notification callback.
	///
	/// @param proc The client's callback function.
	///
	/// @param context Client-provided context for the callback.
	///
	/// @param limit A limit on the number of notifications to be delivered.
	
	static void SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 );
	
	//  --------------------------------------------------------------------------------------------
	/// @brief SetErrorCallback() registers an error notification callback.
	///
	/// @param proc The client's callback function.
	///
	/// @param context Client-provided context for the callback.
	///
	/// @param limit A limit on the number of notifications to be delivered.
	
	void SetErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 );
	
	//  --------------------------------------------------------------------------------------------
	/// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no
	///        effect if an error notification callback function is not registered. 
	///
	/// @param limit A limit on the number of notifications to be delivered.
	
	void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 );

    /// @}

    // =============================================================================================

    XMPMetaRef xmpRef;  // *** Should be private, see below.

private:

#if 0	// *** VS.Net and gcc seem to not handle the friend declarations properly.
    friend class TXMPIterator <class tStringObj>;
    friend class TXMPUtils <class tStringObj>;
#endif

	static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen );

};  // class TXMPMeta

#endif  // __TXMPMeta_hpp__
