// balxml_elementattribute.h                                          -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALXML_ELEMENTATTRIBUTE
#define INCLUDED_BALXML_ELEMENTATTRIBUTE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide the properties of an attribute in an XML element tag.
//
//@CLASSES:
// balxml::ElementAttribute: Properties of an attribute in an XML element tag.
//
//@SEE_ALSO: balxml_reader, balxml_prefixstack
//
//@DESCRIPTION: This component provides a class, 'balxml::ElementAttribute',
// that encapsulates the name, namespace, and value of an attribute within an
// element in an XML document, along with a flag indicating whether the value
// comes from the default value in the document's schema.  There are accessors
// to return the attribute name as a qualified name, namespace prefix,
// namespace URI, namespace ID (in a namespace registry), and local name
// (without the prefix).
//
// If a namespace prefix stack and a qualified name are provided at
// construction (or via the 'reset' method), then all of the other facets of
// the name can be computed automatically.  For efficiency reasons, these
// various decompositions of the attribute name are computed only when needed
// and cached within the 'balxml::ElementAttribute' structure.  The caller can
// supply any or all of these computed facets in the constructor or 'reset'
// method.  Explicitly supplying a facet prevents it from being computed, thus
// saving processing time.  If the object is constructed with explicit values
// for all facets of the name, then the prefix stack is never used and may be
// null.  If an explicitly-supplied facet differs from the value that would
// have been computed otherwise, then the facets returned by the accessors
// will be inconsistent with one-another.  This inconsistency is deliberately
// permitted so that an program may construct a 'balxml::ElementAttribute'
// even when some parts of the name are not yet known, e.g., if the prefix URI
// is known but the qualified name is not.  If the qualified name facet is
// zero, then no other facets will be computed -- their original values will be
// returned from the accessors.
//
///Not Value Semantic
///------------------
// 'balxml::ElementAttribute' is not a value-semantic class.  It provides copy
// construction and assignment, but does not provide equality or persistence
// operators.  Copying a 'balxml::ElementAttribute' object copies all of its
// pointer facets but does not make copies of the pointed-to strings or
// objects.  The presence of copy operations makes it possible to use
// 'balxml::ElementAttribute' with container class templates.
//
// A 'balxml::ElementAttribute' object does not own any of its pointer facets
// and it performs no memory management.  This means that a
// 'balxml::ElementAttribute' object can be invalidated by modifying or
// deleting any of the pointed-to entities.  Calling an accessor on an invalid
// 'balxml::ElementAttribute' is unsafe because the accessor may return an
// invalid pointer.  However an invalid object may be safely destroyed or
// reset.  Components that set or return a 'balxml::ElementAttribute' object
// should provide clear documentation describing the events that will cause
// the resulting 'balxml::ElementAttribute' object to become invalid (see
// 'balxml::Reader').
//
///Thread Safety
///-------------
// It is safe to access or modify two 'balxml::ElementAttribute' objects
// simultaneously, each from a separate thread.  It is safe to access a single
// 'balxml::ElementAttribute' object simultaneously from two or more separate
// threads, provided no other thread is simultaneously modifying the object.
// It is not safe to access or modify a 'balxml::ElementAttribute' object in
// one thread while another thread modifies the same object.
//
// A 'balxml::ElementAttribute' object holds pointers to objects that it does
// not own and which can be modified independently of the
// 'balxml::ElementAttribute' object.  It is not safe to modify or delete any
// of these object in one thread while accessing or modifying the
// 'balxml::ElementAttribute' object in another thread.
//
///Usage
///-----
// The following function parses an XML-style attribute assignment in the form
// "qname='value'", where 'qname' can be a qualified name in the form,
// "prefix:localName".  The prefix (if any) must be registered in the specified
// 'prefixStack' object.  Either single or double quotes may be used to
// enclose the attribute value.  The parsed attribute is stored in the
// specified 'attribute' object of type 'balxml::ElementAttribute'.  Note that
// this function modifies the input string by inserting null characters,
// rather than copying the component parts into allocated memory.  This is a
// realistic interface for a function used within an XML parser that has
// already copied the XML stream into allocated memory.  Also note that this
// function does not interpret character escapes such as "&amp;".
//..
//  int parseAttribute(balxml::ElementAttribute  *attribute,
//                     char                     *attributeString,
//                     const balxml::PrefixStack *prefixStack)
//  {
//..
// First, find the end of the qualified name, i.e., the '=' character:
//..
//      char *qname    = attributeString;
//      char *equalPtr = bsl::strchr(qname, '=');
//      if (0 == equalPtr) {
//          return -1;
//      }
//..
// Then find out which quote character is used to start the value string
//..
//      char quote = *(equalPtr + 1);
//      if (quote != '\'' && quote != '"') {
//          return -1;
//      }
//..
// The value string starts after the opening quote and extends until a
// matching quote:
//..
//      char *value    = equalPtr + 2;
//      char *endValue = bsl::strchr(value, quote);
//      if (0 == endValue) {
//          return -1;
//      }
//..
// Once we have successfully parsed the string, chop it into pieces by putting
// a null terminator at the end of the qualified name and at the end of the
// value string:
//..
//      *equalPtr = '\0';    // Terminate qualified name
//      *endValue = '\0';    // Terminate value string
//..
// Use the prefix stack, qname, and value to set the attribute object.  All
// other arguments are defaulted and will be computed as needed.
//..
//      attribute->reset(prefixStack, qname, value);
//      return 0;
//  }
//..
// Before calling the 'parseAttribute' function, it is necessary to create a
// namespace registry and prefix stack, as well as to register one or more
// prefixes:
//..
//  int main()
//  {
//      balxml::NamespaceRegistry registry;
//      balxml::PrefixStack prefixes(&registry);
//      int cal = prefixes.pushPrefix("cal",
//                                "http://www.bloomberg.com/schemas/calendar");
//      prefixes.pushPrefix("math", "http://www.bloomberg.com/schemas/math");
//      prefixes.pushPrefix("",  // Default namespace
//                          "http://www.bloomberg.com/schemas/app");
//..
// Now we can parse an attribute string and the 'balxml::ElementAttribute'
// object will provide detailed information about it.
//..
//      char attrStr1[] = "cal:date='12-07-2006'";
//      balxml::ElementAttribute attribute1;
//      assert(attribute1.isNull());
//      parseAttribute(&attribute1, attrStr1, &prefixes);
//
//      assert(0 == bsl::strcmp("cal:date", attribute1.qualifiedName()));
//      assert(0 == bsl::strcmp("12-07-2006", attribute1.value()));
//      assert(0 == bsl::strcmp("date", attribute1.localName()));
//      assert(0 == bsl::strcmp("cal", attribute1.prefix()));
//      assert(cal == attribute1.namespaceId());
//      assert(0 == bsl::strcmp("http://www.bloomberg.com/schemas/calendar",
//                              attribute1.namespaceUri()));
//      assert(0 == attribute1.flags());
//..
// Results are slightly different when the attribute name has no prefix:
//..
//      char attrStr2[] = "name=\"Bloomberg, L.P.\"";
//      balxml::ElementAttribute attribute2;
//      parseAttribute(&attribute2, attrStr2, &prefixes);
//
//      assert(0 == bsl::strcmp("name", attribute2.qualifiedName()));
//      assert(0 == bsl::strcmp("Bloomberg, L.P.", attribute2.value()));
//      assert(0 == bsl::strcmp("name", attribute2.localName()));
//..
// As per the XML namespace standard, an attribute with no namespace prefix
// does NOT inherit the default namespace but rather has NO namespace:
//..
//      assert(0 == bsl::strcmp("", attribute2.prefix()));
//      assert(-1 == attribute2.namespaceId());
//      assert(0 == bsl::strcmp("", attribute2.namespaceUri()));
//      assert(0 == attribute2.flags());
//..
// A 'balxml::ElementAttribute' does not need to be generated by parsing XML
// code.  If a specific facet of the object is set to a non-null value, then
// that value will be returned by the corresponding accessor even if it is
// inconsistent with the other values in the object.  For example, the
// following constructs a valid 'balxml::ElementAttribute' object, even though
// the prefix value does not agree with the qualified name:
//..
//      balxml::ElementAttribute attribute3(&prefixes,
//                                         "math:product", "4.5", "cal");
//..
// There is no consistency checking, and the mismatched prefix is simply
// returned by the accessor:
//..
//      assert(0 == bsl::strcmp("math:product", attribute3.qualifiedName()));
//      assert(0 == bsl::strcmp("product", attribute3.localName()));
//      assert(0 == bsl::strcmp("cal", attribute3.prefix()));
//..
// Note that the ability to create inconsistent objects is a deliberate
// feature.  It allows parsers to construct 'balxml::Attribute' objects before
// all information is known, e.g., before the namespace is registered with the
// prefix stack.  Consistency checking also reduces performance.
//..
//      return 0;
//  }
//..

#include <balscm_version.h>

#include <bslmf_istriviallycopyable.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsl_climits.h>
#include <bsl_ostream.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace balxml {

class PrefixStack;

                           // ======================
                           // class ElementAttribute
                           // ======================

class ElementAttribute {
    // Class to represent the properties of an attribute in an XML element tag.
    // Note that this class is not value semantic and does not own any of its
    // pointer values.  The owner of the arguments used to set the value of a
    // 'ElementAttribute' is responsible for ensuring that the values remain
    // valid or else must document the conditions that will make the values
    // invalid.  Some facets are computed the first time that they are needed.
    // To avoid extra processing, the caller may supply otherwise-computed
    // facets at construction or by calling 'reset'.  Facets provided by the
    // caller are not checked to ensure that they are consistent with one
    // another.

    // MEMBER VARIABLES

    // Mutable members may be used to cache computed results.
    const PrefixStack  *d_prefixStack;   // Held, not owned
    const char         *d_qualifiedName; // Held, not owned
    const char         *d_value;         // Held, not owned
    mutable const char *d_prefix;        // Held, not owned
    mutable const char *d_localName;     // Held, not owned
    mutable int         d_namespaceId;
    mutable const char *d_namespaceUri;  // Held, not owned
    unsigned            d_flags;

  public:
    // FLAGS

    // The flags property should be set to the bitwise-OR of one or more of
    // the following values:
    enum {
        k_ATTR_NO_FLAGS   = 0x0000,
            // No flags set.

        k_ATTR_IS_DEFAULT = 0x0001,
            // Set this bit if the value for this attribute object was
            // generated from the default attribute value in the DTD or
            // schema.

        k_ATTR_IS_NSDECL  = 0x0002,
            // Set this bit if this attribute represents a namespace
            // declaration.

        k_ATTR_IS_XSIDECL = 0x0004
            // Set this bit if this attribute represents a XML schema instance
            // declaration.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
      , BAEXML_ATTR_NO_FLAGS = k_ATTR_NO_FLAGS
      , BAEXML_ATTR_IS_DEFAULT = k_ATTR_IS_DEFAULT
      , BAEXML_ATTR_IS_NSDECL = k_ATTR_IS_NSDECL
      , BAEXML_ATTR_IS_XSIDECL = k_ATTR_IS_XSIDECL
      , ATTR_NO_FLAGS   = k_ATTR_NO_FLAGS
      , ATTR_IS_DEFAULT = k_ATTR_IS_DEFAULT
      , ATTR_IS_NSDECL  = k_ATTR_IS_NSDECL
#endif // BDE_OMIT_INTERNAL_DEPRECATED
    };

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ElementAttribute,
                                   bsl::is_trivially_copyable);

    // PUBLIC CREATORS
    ElementAttribute();
        // Construct a null attribute object: Set all string facets to zero,
        // the namespace ID to 'INT_MIN', and flags to zero.

    ElementAttribute(const PrefixStack *prefixStack,
                     const char        *qualifiedName,
                     const char        *value,
                     const char        *prefix = 0,
                     const char        *localName = 0,
                     int                namespaceId = INT_MIN,
                     const char        *namespaceUri = 0,
                     unsigned           flags = 0);
        // Construct an attribute object with the specified, 'prefixStack',
        // 'qualifiedName', and 'value', with optionally specified 'prefix',
        // 'localName', 'namespaceId', 'namespaceUri', and 'flags'.  Except
        // for 'flags', if any of the optional arguments are null (for
        // pointers) or 'INT_MIN' (for integer arguments), then the
        // corresponding facet will computed from the other arguments on an
        // as-needed basis.  If all of the optional arguments are given
        // non-null, non-'INT_MIN' values, then 'prefixStack' is unused and
        // may be null.  Arguments are permitted to be inconsistent with one
        // another (e.g., 'prefix' may not match the beginning of
        // 'qualifiedName') and will produce an object for with facets that
        // are inconsistent with one another.  The constructed object will
        // become invalid if any of the supplied pointers is invalidated
        // during its lifetime (or before it is reset).  An invalid object may
        // be destroyed or reset, but any other access yields undefined
        // behavior.

#ifdef DOXYGEN
    // For efficiency, use compiler-generated copy constructor, destructor,
    // and assignment, but document them as though they were explicitly
    // declared:

    ElementAttribute(const ElementAttribute& other);
        // Construct a copy of the specified 'other' attribute object.  The
        // copy will have pointers to the same strings and prefix table as
        // 'other'.

    ~ElementAttribute();
        // Destroy this attribute object.  The targets of the pointers used to
        // construct or reset this object are not owned by this object and are
        // not deallocated.  It is safe to destroy a 'ElementAttribute'
        // object that is in an invalid state.

    ElementAttribute operator=(const ElementAttribute& rhs);
        // Assign this attribute object the value of 'rhs'.  This object will
        // have pointers to the same strings and prefix table as 'other'.
#endif

    void reset();
        // Reset this object to the default-constructed state.

    void reset(const PrefixStack *prefixStack,
               const char        *qualifiedName,
               const char        *value,
               const char        *prefix = 0,
               const char        *localName = 0,
               int                namespaceId = INT_MIN,
               const char        *namespaceUri = 0,
               unsigned           flags = 0);
        // Reset this attribute object with the specified, 'prefixStack',
        // 'qualifiedName', and 'value', with optionally specified 'prefix',
        // 'localName', 'namespaceId', 'namespaceUri', and 'flags'.  Except
        // for 'flags', if any of the optional arguments are null (for
        // pointers) or 'INT_MIN' (for integer arguments), then the
        // corresponding facet will computed from the other arguments on an
        // as-needed basis.  If all of the optional arguments are given
        // non-null, non-'INT_MIN' values, then 'prefixStack' is unused and
        // may be null.  Arguments are permitted to be inconsistent with one
        // another (e.g., 'prefix' may not match the beginning of
        // 'qualifiedName') and will produce an object for with facets that
        // are inconsistent with one another.  This object will become invalid
        // if any of the supplied pointers is invalidated during its lifetime
        // (or before it is reset).  An invalid object may be destroyed or
        // reset, but any other access yields undefined behavior.

    // ACCESSORS
    const PrefixStack *prefixStack() const;
        // Return the value of 'prefixStack' specified at the last call to
        // 'reset' or the constructor or 0 if no prefix stack was specified.

    const char *qualifiedName() const;
        // Return the value of 'qualifiedName' specified at the last call to
        // 'reset' or the constructor or 0 if no qualified name was specified.

    const char *value() const;
        // Return the value of 'value' specified at the last call to 'reset'
        // or the constructor or 0 if no value was specified.

    const char *prefix() const;
        // Return the value of 'prefix' specified at the last call to 'reset'
        // or the constructor, if non-zero, else return a copy of the prefix
        // portion of the qualified name.  More precisely, return a copy of
        // the portion of 'qualifiedName()' up to, but not including, the
        // colon.  Return an empty string if the qualified name has no colon
        // or if 'prefixStack()' returns zero.

    const char *localName() const;
        // Return the value of 'localName' specified at the last call to
        // 'reset' or the constructor, if non-zero, else return the local part
        // of the qualified name.  More precisely, return the portion of
        // 'qualifiedName()' after the colon, if any, or the entire
        // 'qualifiedName()' if there is no colon.

    int namespaceId() const;
        // Return the value of 'localName' specified at the last call to
        // 'reset' or the constructor, if not 'INT_MIN', else return the ID
        // returned by the prefix stack for the current value of 'prefix()'.
        // Return -1 if 'prefix()' is an empty string, 'prefixStack()' is
        // zero, or 'prefix()' is not active in the prefix stack.  Note that,
        // as per the XML namespace standard, an empty prefix does *NOT* refer
        // to the default namespace, but rather refers to *NO* namespace.

    const char *namespaceUri() const;
        // Return the value of 'namespaceUri' specified at the last call to
        // 'reset' or the constructor, if non-zero, else return the URI
        // returned by the prefix stack for the current value of 'prefix()'.
        // Return an empty string if 'prefix()' is an empty string,
        // 'prefixStack()' is zero, or 'prefix()' is not active in the prefix
        // stack.  Note that, as per the XML namespace standard, an empty
        // prefix does *NOT* refer to the default namespace, but rather refers
        // to *NO* namespace.

    unsigned flags() const;
        // Return the value of 'flags' specified at the last call to 'reset'
        // or the constructor, if non-zero, or zero otherwise.  Pre-defined
        // flags are enumerated in the class definition.

    bool isNull() const;
        // Return true if this object is null.  More precisely, return true if
        // 'qualifiedName()' returns 0.  A default-constructed object will be
        // null, as will an object that was reset with no arguments.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the
        // (absolute value of) the optionally specified indentation 'level'
        // and return a reference to 'stream'.  If 'level' is specified,
        // optionally specify 'spacesPerLevel', the number of spaces per
        // indentation level for this and all of its nested objects.  If
        // 'level' is negative, suppress indentation of the first line.  If
        // 'spacesPerLevel' is negative, format the entire output on one line.
        // If 'stream' is not valid on entry, this operation has no effect.
        // Facet values that have not yet been computed are represented by
        // "<null>" in the resulting stream.
};

// FREE OPERATORS
inline
bsl::ostream& operator<<(bsl::ostream&           os,
                         const ElementAttribute& attribute);
    // Write the contents of the specified 'attribute' object to the specified
    // 'os' in human-readable form.  Attributes that have not yet been
    // computed are not computed by this function.

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

                           // ----------------------
                           // class ElementAttribute
                           // ----------------------

// ACCESSORS
inline
const PrefixStack *ElementAttribute::prefixStack() const
{
    return d_prefixStack;
}

inline
const char *ElementAttribute::qualifiedName() const
{
    return d_qualifiedName;
}

inline
const char *ElementAttribute::value() const
{
    return d_value;
}

inline
unsigned ElementAttribute::flags() const
{
    return d_flags;
}

inline
bool ElementAttribute::isNull() const
{
    return 0 == d_qualifiedName;
}

}  // close package namespace

// FREE OPERATORS
inline
bsl::ostream& balxml::operator<<(bsl::ostream&           os,
                                 const ElementAttribute& attribute)
{
    return attribute.print(os);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------